X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FGlobalFunctions.php;h=b66589a128599f7ab0b2122656e69913672d6f22;hb=7d9261ce1e920e6f7d2add78a4b12d1c7485d086;hp=878f3bcf89a6c97ee4a6ec6564a39aee6e3ddff9;hpb=6779a2cc137aef2e125ded723640686fdfa0db38;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 878f3bcf89..b66589a128 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -72,6 +72,54 @@ if ( !function_exists( 'mb_strlen' ) ) { } } + +if( !function_exists( 'mb_strpos' ) ) { + /** + * Fallback implementation of mb_strpos, hardcoded to UTF-8. + * @param $haystack String + * @param $needle String + * @param $offset String: optional start position + * @param $encoding String: optional encoding; ignored + * @return int + */ + function mb_strpos( $haystack, $needle, $offset = 0, $encoding="" ) { + $needle = preg_quote( $needle, '/' ); + + $ar = array(); + preg_match( '/'.$needle.'/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset ); + + if( isset( $ar[0][1] ) ) { + return $ar[0][1]; + } else { + return false; + } + } +} + +if( !function_exists( 'mb_strrpos' ) ) { + /** + * Fallback implementation of mb_strrpos, hardcoded to UTF-8. + * @param $haystack String + * @param $needle String + * @param $offset String: optional start position + * @param $encoding String: optional encoding; ignored + * @return int + */ + function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = "" ) { + $needle = preg_quote( $needle, '/' ); + + $ar = array(); + preg_match_all( '/'.$needle.'/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset ); + + if( isset( $ar[0] ) && count( $ar[0] ) > 0 && + isset( $ar[0][count($ar[0])-1][1] ) ) { + return $ar[0][count($ar[0])-1][1]; + } else { + return false; + } + } +} + if ( !function_exists( 'array_diff_key' ) ) { /** * Exists in PHP 5.1.0+ @@ -89,6 +137,19 @@ if ( !function_exists( 'array_diff_key' ) ) { } } +// Support for Wietse Venema's taint feature +if ( !function_exists( 'istainted' ) ) { + function istainted( $var ) { + return 0; + } + function taint( $var, $level = 0 ) {} + function untaint( $var, $level = 0 ) {} + define( 'TC_HTML', 1 ); + define( 'TC_SHELL', 1 ); + define( 'TC_MYSQL', 1 ); + define( 'TC_PCRE', 1 ); + define( 'TC_SELF', 1 ); +} /// @endcond @@ -234,7 +295,7 @@ function wfDebug( $text, $logonly = false ) { /** * Send a line giving PHP memory usage. - * @param $exact Bool : print exact values instead of kilobytes (default: false) + * @param $exact Bool: print exact values instead of kilobytes (default: false) */ function wfDebugMem( $exact = false ) { $mem = memory_get_usage(); @@ -337,12 +398,14 @@ function wfErrorLog( $text, $file ) { */ function wfLogProfilingData() { global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; - global $wgProfiler, $wgUser; - if ( !isset( $wgProfiler ) ) - return; - + global $wgProfiler, $wgProfileLimit, $wgUser; + # Profiling must actually be enabled... + if( !isset( $wgProfiler ) ) return; + # Get total page request time $now = wfTime(); $elapsed = $now - $wgRequestTime; + # Only show pages that longer than $wgProfileLimit time (default is 0) + if( $elapsed <= $wgProfileLimit ) return; $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed ); $forward = ''; if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) @@ -543,7 +606,7 @@ function wfMsgNoDBForContent( $key ) { * @param $forContent Boolean * @return String: the requested message. */ -function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) { +function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) { wfProfileIn( __METHOD__ ); $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); $message = wfMsgReplaceArgs( $message, $args ); @@ -555,7 +618,7 @@ function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = * This function provides the message source for messages to be edited which are *not* stored in the database. * @param $key String: */ -function wfMsgWeirdKey ( $key ) { +function wfMsgWeirdKey( $key ) { $source = wfMsgGetKey( $key, false, true, false ); if ( wfEmptyMsg( $key, $source ) ) return ""; @@ -565,11 +628,11 @@ function wfMsgWeirdKey ( $key ) { /** * Fetch a message string value, but don't replace any keys yet. - * @param string $key - * @param bool $useDB - * @param string $langcode Code of the language to get the message for, or - * behaves as a content language switch if it is a - * boolean. + * @param $key String + * @param $useDB Bool + * @param $langCode String: Code of the language to get the message for, or + * behaves as a content language switch if it is a boolean. + * @param $transform Boolean: whether to parse magic words, etc. * @return string * @private */ @@ -604,8 +667,8 @@ function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) { /** * Replace message parameter keys on the given formatted output. * - * @param string $message - * @param array $args + * @param $message String + * @param $args Array * @return string * @private */ @@ -636,7 +699,7 @@ function wfMsgReplaceArgs( $message, $args ) { * to pre-escape them if you really do want plaintext, or just wrap * the whole thing in htmlspecialchars(). * - * @param string $key + * @param $key String * @param string ... parameters * @return string */ @@ -653,7 +716,7 @@ function wfMsgHtml( $key ) { * to pre-escape them if you really do want plaintext, or just wrap * the whole thing in htmlspecialchars(). * - * @param string $key + * @param $key String * @param string ... parameters * @return string */ @@ -666,8 +729,8 @@ function wfMsgWikiHtml( $key ) { /** * Returns message in the requested format - * @param string $key Key of the message - * @param array $options Processing rules. Can take the following options: + * @param $key String: key of the message + * @param $options Array: processing rules. Can take the following options: * parse: parses wikitext to html * parseinline: parses wikitext to html and removes the surrounding * p's added by parser or tidy @@ -693,12 +756,12 @@ function wfMsgExt( $key, $options ) { foreach( $options as $arrayKey => $option ) { if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) { # An unknown index, neither numeric nor "language" - trigger_error( "wfMsgExt called with incorrect parameter key $arrayKey", E_USER_WARNING ); + wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING ); } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter', 'parsemag', 'content' ) ) ) { # A numeric index with unknown value - trigger_error( "wfMsgExt called with incorrect parameter $option", E_USER_WARNING ); + wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING ); } } @@ -792,7 +855,7 @@ function wfErrorExit() { /** * Print a simple message and die, returning nonzero to the shell if any. * Plain die() fails to return nonzero to the shell if you pass a string. - * @param string $msg + * @param $msg String */ function wfDie( $msg='' ) { echo $msg; @@ -803,7 +866,7 @@ function wfDie( $msg='' ) { * Throw a debugging exception. This function previously once exited the process, * but now throws an exception instead, with similar results. * - * @param string $msg Message shown when dieing. + * @param $msg String: message shown when dieing. */ function wfDebugDieBacktrace( $msg = '' ) { throw new MWException( $msg ); @@ -837,21 +900,21 @@ function wfHostname() { return $host; } - /** - * Returns a HTML comment with the elapsed time since request. - * This method has no side effects. - * @return string - */ - function wfReportTime() { - global $wgRequestTime, $wgShowHostnames; +/** + * Returns a HTML comment with the elapsed time since request. + * This method has no side effects. + * @return string + */ +function wfReportTime() { + global $wgRequestTime, $wgShowHostnames; - $now = wfTime(); - $elapsed = $now - $wgRequestTime; + $now = wfTime(); + $elapsed = $now - $wgRequestTime; - return $wgShowHostnames - ? sprintf( "", wfHostname(), $elapsed ) - : sprintf( "", $elapsed ); - } + return $wgShowHostnames + ? sprintf( "", wfHostname(), $elapsed ) + : sprintf( "", $elapsed ); +} /** * Safety wrapper for debug_backtrace(). @@ -860,18 +923,35 @@ function wfHostname() { * 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). + * Will return an empty array if Zend Optimizer is detected or if + * debug_backtrace is disabled, otherwise the output from + * debug_backtrace() (trimmed). * * @return array of backtrace information */ function wfDebugBacktrace() { + static $disabled = null; + if( extension_loaded( 'Zend Optimizer' ) ) { wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" ); return array(); - } else { - return array_slice( debug_backtrace(), 1 ); } + + if ( is_null( $disabled ) ) { + $disabled = false; + $functions = explode( ',', ini_get( 'disable_functions' ) ); + $functions = array_map( 'trim', $functions ); + $functions = array_map( 'strtolower', $functions ); + if ( in_array( 'debug_backtrace', $functions ) ) { + wfDebug( "debug_backtrace is in disabled_functions\n" ); + $disabled = true; + } + } + if ( $disabled ) { + return array(); + } + + return array_slice( debug_backtrace(), 1 ); } function wfBacktrace() { @@ -927,7 +1007,8 @@ function wfBacktrace() { */ function wfShowingResults( $offset, $limit ) { global $wgLang; - return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) ); + return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), + $wgLang->formatNum( $offset+1 ) ); } /** @@ -935,18 +1016,29 @@ function wfShowingResults( $offset, $limit ) { */ function wfShowingResultsNum( $offset, $limit, $num ) { global $wgLang; - return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $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 ) ); } /** - * @todo document + * Generate (prev x| next x) (20|50|100...) type links for paging + * @param $offset String + * @param $limit Integer + * @param $link String + * @param $query String: optional URL query parameter string + * @param $atend Bool: optional param for specified if this is the last page */ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { global $wgLang; $fmtLimit = $wgLang->formatNum( $limit ); - $prev = wfMsg( 'prevn', $fmtLimit ); - $next = wfMsg( 'nextn', $fmtLimit ); - + // FIXME: Why on earth this needs one message for the text and another one for tooltip?? + # Get prev/next link display text + $prev = wfMsgExt( 'prevn', array('parsemag','escape'), $fmtLimit ); + $next = wfMsgExt( 'nextn', array('parsemag','escape'), $fmtLimit ); + # Get prev/next link title text + $pTitle = wfMsgExt( 'prevn-title', array('parsemag','escape'), $fmtLimit ); + $nTitle = wfMsgExt( 'nextn-title', array('parsemag','escape'), $fmtLimit ); + # Fetch the title object if( is_object( $link ) ) { $title =& $link; } else { @@ -955,44 +1047,58 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { return false; } } - - if ( 0 != $offset ) { + # Make 'previous' link + if( 0 != $offset ) { $po = $offset - $limit; - if ( $po < 0 ) { $po = 0; } + $po = max($po,0); $q = "limit={$limit}&offset={$po}"; - if ( '' != $query ) { $q .= '&'.$query; } - $plink = '{$prev}"; - } else { $plink = $prev; } - + if( $query != '' ) { + $q .= '&'.$query; + } + $plink = '{$prev}"; + } else { + $plink = $prev; + } + # Make 'next' link $no = $offset + $limit; - $q = 'limit='.$limit.'&offset='.$no; - if ( '' != $query ) { $q .= '&'.$query; } - - if ( $atend ) { + $q = "limit={$limit}&offset={$no}"; + if( $query != '' ) { + $q .= '&'.$query; + } + if( $atend ) { $nlink = $next; } else { - $nlink = '{$next}"; + $nlink = '{$next}"; } - $nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' . - wfNumLink( $offset, 50, $title, $query ) . ' | ' . - wfNumLink( $offset, 100, $title, $query ) . ' | ' . - wfNumLink( $offset, 250, $title, $query ) . ' | ' . - wfNumLink( $offset, 500, $title, $query ); - - return wfMsg( 'viewprevnext', $plink, $nlink, $nums ); + # Make links to set number of items per page + $nums = $wgLang->pipeList( array( + wfNumLink( $offset, 20, $title, $query ), + wfNumLink( $offset, 50, $title, $query ), + wfNumLink( $offset, 100, $title, $query ), + wfNumLink( $offset, 250, $title, $query ), + wfNumLink( $offset, 500, $title, $query ) + ) ); + return wfMsgHtml( 'viewprevnext', $plink, $nlink, $nums ); } /** - * @todo document + * Generate links for (20|50|100...) items-per-page links + * @param $offset String + * @param $limit Integer + * @param $title Title + * @param $query String: optional URL query parameter string */ -function wfNumLink( $offset, $limit, &$title, $query = '' ) { +function wfNumLink( $offset, $limit, $title, $query = '' ) { global $wgLang; - if ( '' == $query ) { $q = ''; } - else { $q = $query.'&'; } - $q .= 'limit='.$limit.'&offset='.$offset; - + if( $query == '' ) { + $q = ''; + } else { + $q = $query.'&'; + } + $q .= "limit={$limit}&offset={$offset}"; $fmtLimit = $wgLang->formatNum( $limit ); - $s = '{$fmtLimit}"; + $lTitle = wfMsgExt('shown-title',array('parsemag','escape'),$limit); + $s = '{$fmtLimit}"; return $s; } @@ -1041,7 +1147,7 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { * not filter out characters which have special meaning only at the * start of a line, such as "*". * - * @param string $text Text to be escaped + * @param $text String: text to be escaped */ function wfEscapeWikiText( $text ) { $text = str_replace( @@ -1125,20 +1231,21 @@ function wfArrayToCGI( $array1, $array2 = NULL ) if ( '' != $cgi ) { $cgi .= '&'; } - if(is_array($value)) - { + if ( is_array( $value ) ) { $firstTime = true; - foreach($value as $v) - { - $cgi .= ($firstTime ? '' : '&') . + foreach ( $value as $v ) { + $cgi .= ( $firstTime ? '' : '&') . urlencode( $key . '[]' ) . '=' . urlencode( $v ); $firstTime = false; } - } - else + } else { + if ( is_object( $value ) ) { + $value = $value->__toString(); + } $cgi .= urlencode( $key ) . '=' . urlencode( $value ); + } } } return $cgi; @@ -1151,7 +1258,7 @@ function wfArrayToCGI( $array1, $array2 = NULL ) * arrays. Of course, keys and values are urldecode()d. Don't try passing in- * valid query strings, or it will explode. * - * @param $query string Query string + * @param $query String: query string * @return array Array version of input */ function wfCgiToArray( $query ) { @@ -1176,11 +1283,14 @@ function wfCgiToArray( $query ) { * 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 + * @param $url String + * @param $query Mixed: string or associative array * @return string */ function wfAppendQuery( $url, $query ) { + if ( is_array( $query ) ) { + $query = wfArrayToCGI( $query ); + } if( $query != '' ) { if( false === strpos( $url, '?' ) ) { $url .= '?'; @@ -1193,9 +1303,13 @@ function wfAppendQuery( $url, $query ) { } /** - * 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 + * Expand a potentially local URL to a fully-qualified URL. Assumes $wgServer + * is correct. Also doesn't handle any type of relative URL except one + * starting with a single "/": this won't work with current-path-relative URLs + * like "subdir/foo.html", protocol-relative URLs like + * "//en.wikipedia.org/wiki/", etc. TODO: improve this! + * + * @param $url String: either fully-qualified or a local path + query * @return string Fully-qualified URL */ function wfExpandUrl( $url ) { @@ -1330,12 +1444,16 @@ function wfMerge( $old, $mine, $yours, &$result ){ /** * Returns unified plain-text diff of two texts. * Useful for machine processing of diffs. - * @param $before string The text before the changes. - * @param $after string The text after the changes. - * @param $params string Command-line options for the diff command. - * @return string Unified diff of $before and $after + * @param $before String: the text before the changes. + * @param $after String: the text after the changes. + * @param $params String: command-line options for the diff command. + * @return String: unified diff of $before and $after */ function wfDiff( $before, $after, $params = '-u' ) { + if ($before == $after) { + return ''; + } + global $wgDiff; # This check may also protect against code injection in @@ -1441,7 +1559,7 @@ function wfHttpError( $code, $label, $desc ) { * Note that some PHP configuration options may add output buffer * layers which cannot be removed; these are left in place. * - * @param bool $resetGzipEncoding + * @param $resetGzipEncoding Bool */ function wfResetOutputBuffers( $resetGzipEncoding=true ) { if( $resetGzipEncoding ) { @@ -1526,8 +1644,8 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) { * Returns the matching MIME type (or wildcard) if a match, otherwise * NULL if no match. * - * @param string $type - * @param array $avail + * @param $type String + * @param $avail Array * @return string * @private */ @@ -1552,8 +1670,8 @@ function mimeTypeMatch( $type, $avail ) { * array of type to preference (preference is a float between 0.0 and 1.0). * Wildcards in the types are acceptable. * - * @param array $cprefs Client's acceptable type list - * @param array $sprefs Server's offered types + * @param $cprefs Array: client's acceptable type list + * @param $sprefs Array: server's offered types * @return string * * @todo FIXME: doesn't handle params like 'text/plain; charset=UTF-8' @@ -1693,12 +1811,18 @@ define('TS_ORACLE', 6); define('TS_POSTGRES', 7); /** - * @param mixed $outputtype A timestamp in one of the supported formats, the - * function will autodetect which format is supplied - * and act accordingly. - * @return string Time in the format specified in $outputtype + * DB2 format time + */ +define('TS_DB2', 8); + +/** + * @param $outputtype Mixed: A timestamp in one of the supported formats, the + * function will autodetect which format is supplied and act + * accordingly. + * @param $ts Mixed: the timestamp to convert or 0 for the current timestamp + * @return String: in the format specified in $outputtype */ -function wfTimestamp($outputtype=TS_UNIX,$ts=0) { +function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { $uts = 0; $da = array(); if ($ts==0) { @@ -1712,8 +1836,8 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { } elseif (preg_match('/^\d{1,13}$/D',$ts)) { # TS_UNIX $uts = $ts; - } elseif (preg_match('/^\d{1,2}-...-\d\d(?:\d\d)? \d\d\.\d\d\.\d\d/', $ts)) { - # TS_ORACLE + } elseif (preg_match('/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts)) { + # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6 $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3", str_replace("+00:00", "UTC", $ts))); } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da)) { @@ -1750,9 +1874,12 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { case TS_RFC2822: 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'; + return gmdate( 'd-m-Y H:i:s.000000', $uts); + //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'; + case TS_DB2: + return gmdate( 'Y-m-d H:i:s', $uts); default: throw new MWException( 'wfTimestamp() called with illegal output type.'); } @@ -1761,9 +1888,9 @@ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { /** * Return a formatted timestamp, or null if input is null. * For dealing with nullable timestamp columns in the database. - * @param int $outputtype - * @param string $ts - * @return string + * @param $outputtype Integer + * @param $ts String + * @return String */ function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { if( is_null( $ts ) ) { @@ -1776,7 +1903,7 @@ function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { /** * Check if the operating system is Windows * - * @return bool True if it's Windows, False otherwise. + * @return Bool: true if it's Windows, False otherwise. */ function wfIsWindows() { if (substr(php_uname(), 0, 7) == 'Windows') { @@ -1911,7 +2038,7 @@ function &wfGetMimeMagic() { * NOTE: When possible, use the tempfile() function to create temporary * files to avoid race conditions on file creation, etc. * - * @return string + * @return String */ function wfTempDir() { foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) { @@ -1927,16 +2054,23 @@ function wfTempDir() { /** * Make directory, and make all parent directories if they don't exist * - * @param string $dir Full path to directory to create - * @param int $mode Chmod value to use, default is $wgDirectoryMode + * @param $dir String: full path to directory to create + * @param $mode Integer: chmod value to use, default is $wgDirectoryMode + * @param $caller String: optional caller param for debugging. * @return bool */ -function wfMkdirParents( $dir, $mode = null ) { +function wfMkdirParents( $dir, $mode = null, $caller = null ) { global $wgDirectoryMode; + if ( !is_null( $caller ) ) { + wfDebug( "$caller: called wfMkdirParents($dir)" ); + } + if( strval( $dir ) === '' || file_exists( $dir ) ) return true; + $dir = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $dir ); + if ( is_null( $mode ) ) $mode = $wgDirectoryMode; @@ -1971,9 +2105,9 @@ function wfIncrStats( $key ) { } /** - * @param mixed $nr The number to format - * @param int $acc The number of digits after the decimal point, default 2 - * @param bool $round Whether or not to round the value, default true + * @param $nr Mixed: the number to format + * @param $acc Integer: the number of digits after the decimal point, default 2 + * @param $round Boolean: whether or not to round the value, default true * @return float */ function wfPercent( $nr, $acc = 2, $round = true ) { @@ -1984,9 +2118,9 @@ function wfPercent( $nr, $acc = 2, $round = true ) { /** * Encrypt a username/password. * - * @param string $userid ID of the user - * @param string $password Password of the user - * @return string Hashed password + * @param $userid Integer: ID of the user + * @param $password String: password of the user + * @return String: hashed password * @deprecated Use User::crypt() or User::oldCrypt() instead */ function wfEncryptPassword( $userid, $password ) { @@ -2012,9 +2146,9 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { * looked up didn't exist but a XHTML string, this function checks for the * nonexistance of messages by looking at wfMsg() output * - * @param $msg The message key looked up - * @param $wfMsgOut The output of wfMsg*() - * @return bool + * @param $msg String: the message key looked up + * @param $wfMsgOut String: the output of wfMsg*() + * @return Boolean */ function wfEmptyMsg( $msg, $wfMsgOut ) { return $wfMsgOut === htmlspecialchars( "<$msg>" ); @@ -2023,9 +2157,9 @@ function wfEmptyMsg( $msg, $wfMsgOut ) { /** * Find out whether or not a mixed variable exists in a string * - * @param mixed needle - * @param string haystack - * @return bool + * @param $needle String + * @param $str String + * @return Boolean */ function in_string( $needle, $str ) { return strpos( $str, $needle ) !== false; @@ -2040,7 +2174,7 @@ function wfSpecialList( $page, $details ) { /** * Returns a regular expression of url protocols * - * @return string + * @return String */ function wfUrlProtocols() { global $wgUrlProtocols; @@ -2078,8 +2212,8 @@ function wfUrlProtocols() { * * I frickin' hate PHP... :P * - * @param string $setting - * @return bool + * @param $setting String + * @return Bool */ function wfIniGetBool( $setting ) { $val = ini_get( $setting ); @@ -2137,7 +2271,7 @@ function wfShellExec( $cmd, &$retval=null ) { } elseif ( php_uname( 's' ) == 'Windows NT' ) { # This is a hack to work around PHP's flawed invocation of cmd.exe # http://news.php.net/php.internals/21796 - $cmd = '"' . $cmd . '"'; + $cmd = '"' . $cmd . '"'; // FIXME: breaking Vista sp2/PHP 5.2.9(2) } wfDebug( "wfShellExec: $cmd\n" ); @@ -2153,6 +2287,41 @@ function wfShellExec( $cmd, &$retval=null ) { return $output; } +/** + * Executes a shell command in the background. Passes back the PID of the operation + * + * @param $cmd String + */ +function wfShellBackgroundExec( $cmd ){ + wfDebug( "wfShellBackgroundExec: $cmd\n" ); + + if ( ! wfShellExecEnabled() ) { + return "Unable to run external programs"; + } + + $pid = shell_exec( "nohup $cmd > /dev/null & echo $!" ); + return $pid; +} + +/** + * Checks if the current instance can execute a shell command + * + */ +function wfShellExecEnabled(){ + if( wfIniGetBool( 'safe_mode' ) ) { + wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); + return false; + } + $functions = explode( ',', ini_get( 'disable_functions' ) ); + $functions = array_map( 'trim', $functions ); + $functions = array_map( 'strtolower', $functions ); + if ( in_array( 'passthru', $functions ) ) { + wfDebug( "passthru is in disabled_functions\n" ); + return false; + } + return true; +} + /** * Workaround for http://bugs.php.net/bug.php?id=45132 * escapeshellarg() destroys non-ASCII characters if LANG is not a UTF-8 locale @@ -2180,8 +2349,8 @@ function wfInitShellLocale() { * * @see perldoc -f use * - * @param mixed $version The version to check, can be a string, an integer, or - * a float + * @param $req_ver Mixed: the version to check, can be a string, an integer, or + * a float */ function wfUsePHP( $req_ver ) { $php_ver = PHP_VERSION; @@ -2200,8 +2369,8 @@ function wfUsePHP( $req_ver ) { * * @see perldoc -f use * - * @param mixed $version The version to check, can be a string, an integer, or - * a float + * @param $req_ver Mixed: the version to check, can be a string, an integer, or + * a float */ function wfUseMW( $req_ver ) { global $wgVersion; @@ -2225,9 +2394,9 @@ function wfRegexReplacement( $string ) { * PHP's basename() only considers '\' a pathchar on Windows and Netware. * We'll consider it so always, as we don't want \s in our Unix paths either. * - * @param string $path - * @param string $suffix to remove if present - * @return string + * @param $path String + * @param $suffix String: to remove if present + * @return String */ function wfBaseName( $path, $suffix='' ) { $encSuffix = ($suffix == '') @@ -2246,9 +2415,9 @@ function wfBaseName( $path, $suffix='' ) { * 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 + * @param $path String: absolute destination path including target filename + * @param $from String: Absolute source path, directory only + * @return String */ function wfRelativePath( $path, $from ) { // Normalize mixed input on Windows... @@ -2290,8 +2459,9 @@ function wfRelativePath( $path, $from ) { * Backwards array plus for people who haven't bothered to read the PHP manual * XXX: will not darn your socks for you. * - * @param array $array1, [$array2, [...]] - * @return array + * @param $array1 Array + * @param [$array2, [...]] Arrays + * @return Array */ function wfArrayMerge( $array1/* ... */ ) { $args = func_get_args(); @@ -2332,9 +2502,16 @@ function wfMergeErrorArrays(/*...*/) { } /** - * Make a URL index, appropriate for the el_index field of externallinks. + * parse_url() work-alike, but non-broken. Differences: + * + * 1) Does not raise warnings on bad URLs (just returns false) + * 2) Handles protocols that don't use :// (e.g., mailto: and news:) correctly + * 3) Adds a "delimiter" element to the array, either '://' or ':' (see (2)) + * + * @param $url String: a URL to parse + * @return Array: bits of the URL in an associative array, per PHP docs */ -function wfMakeUrlIndex( $url ) { +function wfParseUrl( $url ) { global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php wfSuppressWarnings(); $bits = parse_url( $url ); @@ -2342,12 +2519,12 @@ function wfMakeUrlIndex( $url ) { 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 = ':'; + if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) { + $bits['delimiter'] = '://'; + } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) { + $bits['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'] ) ) { @@ -2358,6 +2535,15 @@ function wfMakeUrlIndex( $url ) { return false; } + return $bits; +} + +/** + * Make a URL index, appropriate for the el_index field of externallinks. + */ +function wfMakeUrlIndex( $url ) { + $bits = wfParseUrl( $url ); + // Reverse the labels in the hostname, convert to lower case // For emails reverse domainpart only if ( $bits['scheme'] == 'mailto' ) { @@ -2379,7 +2565,7 @@ function wfMakeUrlIndex( $url ) { } // Reconstruct the pseudo-URL $prot = $bits['scheme']; - $index = "$prot$delimiter$reversedHost"; + $index = $prot . $bits['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'] ) ) { @@ -2423,12 +2609,12 @@ function wfExplodeMarkup( $separator, $text ) { * 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 - * @param $lowercase bool - * @return string or false on invalid input + * @param $input String: of digits + * @param $sourceBase Integer: 2-36 + * @param $destBase Integer: 2-36 + * @param $pad Integer: 1 or greater + * @param $lowercase Boolean + * @return String or false on invalid input */ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) { $input = strval( $input ); @@ -2505,8 +2691,8 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true /** * Create an object with a given name and an array of construct parameters - * @param string $name - * @param array $p parameters + * @param $name String + * @param $p Array: parameters */ function wfCreateObject( $name, $p ){ $p = array_values( $p ); @@ -2534,9 +2720,9 @@ function wfCreateObject( $name, $p ){ * Alias for modularized function * @deprecated Use Http::get() instead */ -function wfGetHTTP( $url, $timeout = 'default' ) { +function wfGetHTTP( $url ) { wfDeprecated(__FUNCTION__); - return Http::get( $url, $timeout ); + return Http::get( $url ); } /** @@ -2560,7 +2746,7 @@ function wfHttpOnlySafe() { } } } - + return true; } @@ -2568,13 +2754,14 @@ function wfHttpOnlySafe() { * Initialise php session */ function wfSetupSession() { - global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly; + global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, + $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler; 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' ); + } elseif( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) { + # Only set this if $wgSessionHandler isn't null and session.save_handler + # hasn't already been set to the desired value (that causes errors) + ini_set ( 'session.save_handler', $wgSessionHandler ); } $httpOnlySafe = wfHttpOnlySafe(); wfDebugLog( 'cookie', @@ -2600,7 +2787,7 @@ function wfSetupSession() { /** * Get an object from the precompiled serialized directory * - * @return mixed The variable on success, false on failure + * @return Mixed: the variable on success, false on failure */ function wfGetPrecompiledData( $name ) { global $IP; @@ -2625,12 +2812,17 @@ function wfGetCaller( $level = 2 ) { return $caller; } -/** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */ +/** + * Return a string consisting all callers in stack, somewhat useful sometimes + * for profiling specific points + */ function wfGetAllCallers() { return implode('/', array_map('wfFormatStackFrame',array_reverse(wfDebugBacktrace()))); } -/** Return a string representation of frame */ +/** + * Return a string representation of frame + */ function wfFormatStackFrame($frame) { return isset( $frame["class"] )? $frame["class"]."::".$frame["function"]: @@ -2643,6 +2835,7 @@ function wfFormatStackFrame($frame) { function wfMemcKey( /*... */ ) { $args = func_get_args(); $key = wfWikiID() . ':' . implode( ':', $args ); + $key = str_replace( ' ', '_', $key ); return $key; } @@ -2663,16 +2856,12 @@ function wfForeignMemcKey( $db, $prefix /*, ... */ ) { * Get an ASCII string identifying this wiki * This is used as a prefix in memcached keys */ -function wfWikiID( $db = null ) { - if( $db instanceof Database ) { - return $db->getWikiID(); - } else { +function wfWikiID() { global $wgDBprefix, $wgDBname; - if ( $wgDBprefix ) { - return "$wgDBname-$wgDBprefix"; - } else { - return $wgDBname; - } + if ( $wgDBprefix ) { + return "$wgDBname-$wgDBprefix"; + } else { + return $wgDBname; } } @@ -2689,15 +2878,15 @@ function wfSplitWikiID( $wiki ) { /* * 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 $db Integer: 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 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 $groups Mixed: 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 + * @param $wiki String: the wiki ID, or false for the current wiki * * Note: multiple calls to wfGetDB(DB_SLAVE) during the course of one request * will always return the same object, unless the underlying connection or load @@ -2710,8 +2899,7 @@ function &wfGetDB( $db, $groups = array(), $wiki = false ) { /** * Get a load balancer object. * - * @param array $groups List of query groups - * @param string $wiki Wiki ID, or false for the current wiki + * @param $wiki String: wiki ID, or false for the current wiki * @return LoadBalancer */ function wfGetLB( $wiki = false ) { @@ -2728,12 +2916,12 @@ function &wfGetLBFactory() { /** * Find a file. * Shortcut for RepoGroup::singleton()->findFile() - * @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 - * was created at the specified time. - * @param mixed $flags FileRepo::FIND_ flags - * @param boolean $bypass Bypass the file cache even if it could be used + * @param $title Title object or string. May be interwiki. + * @param $time Mixed: requested time for an archived image, or false for the + * current version. An image object will be returned which was + * created at the specified time. + * @param $flags Mixed: FileRepo::FIND_ flags + * @param $bypass Boolean: bypass the file cache even if it could be used * @return File, or false if the file does not exist */ function wfFindFile( $title, $time = false, $flags = 0, $bypass = false ) { @@ -2755,7 +2943,7 @@ function wfLocalFile( $title ) { /** * Should low-performance queries be disabled? * - * @return bool + * @return Boolean */ function wfQueriesMustScale() { global $wgMiserMode; @@ -2769,20 +2957,42 @@ function wfQueriesMustScale() { * Get the path to a specified script file, respecting file * extensions; this is a wrapper around $wgScriptExtension etc. * - * @param string $script Script filename, sans extension - * @return string + * @param $script String: script filename, sans extension + * @return String */ function wfScript( $script = 'index' ) { global $wgScriptPath, $wgScriptExtension; return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; } +/** + * Get the script url. + * + * @return script url + */ +function wfGetScriptUrl(){ + if( isset( $_SERVER['SCRIPT_NAME'] ) ) { + # + # as it was called, minus the query string. + # + # Some sites use Apache rewrite rules to handle subdomains, + # and have PHP set up in a weird way that causes PHP_SELF + # to contain the rewritten URL instead of the one that the + # outside world sees. + # + # If in this mode, use SCRIPT_URL instead, which mod_rewrite + # provides containing the "before" URL. + return $_SERVER['SCRIPT_NAME']; + } else { + return $_SERVER['URL']; + } +} /** * Convenience function converts boolean values into "true" * or "false" (string) values * - * @param bool $value - * @return string + * @param $value Boolean + * @return String */ function wfBoolToStr( $value ) { return $value ? 'true' : 'false'; @@ -2790,42 +3000,9 @@ function wfBoolToStr( $value ) { /** * Load an extension messages file - * - * @param string $extensionName Name of extension to load messages from\for. - * @param string $langcode Language to load messages for, or false for default - * behvaiour (en, content language and user language). - * @since r24808 (v1.11) Using this method of loading extension messages will not work - * on MediaWiki prior to that + * @deprecated */ function wfLoadExtensionMessages( $extensionName, $langcode = false ) { - global $wgExtensionMessagesFiles, $wgMessageCache, $wgLang, $wgContLang; - - #For recording whether extension message files have been loaded in a given language. - static $loaded = array(); - - if( !array_key_exists( $extensionName, $loaded ) ) { - $loaded[$extensionName] = array(); - } - - if ( !isset($wgExtensionMessagesFiles[$extensionName]) ) { - throw new MWException( "Messages file for extensions $extensionName is not defined" ); - } - - if( !$langcode && !array_key_exists( '*', $loaded[$extensionName] ) ) { - # Just do en, content language and user language. - $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], false ); - # Mark that they have been loaded. - $loaded[$extensionName]['en'] = true; - $loaded[$extensionName][$wgLang->getCode()] = true; - $loaded[$extensionName][$wgContLang->getCode()] = true; - # Mark that this part has been done to avoid weird if statements. - $loaded[$extensionName]['*'] = true; - } elseif( is_string( $langcode ) && !array_key_exists( $langcode, $loaded[$extensionName] ) ) { - # Load messages for specified language. - $wgMessageCache->loadMessagesFile( $wgExtensionMessagesFiles[$extensionName], $langcode ); - # Mark that they have been loaded. - $loaded[$extensionName][$langcode] = true; - } } /** @@ -2843,9 +3020,9 @@ function wfGetNull() { /** * Displays a maxlag error * - * @param string $host Server that lags the most - * @param int $lag Maxlag (actual) - * @param int $maxLag Maxlag (requested) + * @param $host String: server that lags the most + * @param $lag Integer: maxlag (actual) + * @param $maxLag Integer: maxlag (requested) */ function wfMaxlagError( $host, $lag, $maxLag ) { global $wgShowHostnames; @@ -2861,19 +3038,23 @@ function wfMaxlagError( $host, $lag, $maxLag ) { } /** - * Throws an E_USER_NOTICE saying that $function is deprecated - * @param string $function + * Throws a warning that $function is deprecated + * @param $function String * @return null */ function wfDeprecated( $function ) { - global $wgDebugLogFile; - if ( !$wgDebugLogFile ) { - return; + static $functionsWarned = array(); + if ( !isset( $functionsWarned[$function] ) ) { + $functionsWarned[$function] = true; + wfWarn( "Use of $function is deprecated.", 2 ); } +} + +function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) { $callers = wfDebugBacktrace(); - if( isset( $callers[2] ) ){ - $callerfunc = $callers[2]; - $callerfile = $callers[1]; + if( isset( $callers[$callerOffset+1] ) ){ + $callerfunc = $callers[$callerOffset+1]; + $callerfile = $callers[$callerOffset]; if( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ){ $file = $callerfile['file'] . ' at line ' . $callerfile['line']; } else { @@ -2883,11 +3064,15 @@ function wfDeprecated( $function ) { if( isset( $callerfunc['class'] ) ) $func .= $callerfunc['class'] . '::'; $func .= @$callerfunc['function']; - $msg = "Use of $function is deprecated. Called from $func in $file"; + $msg .= " [Called from $func in $file]"; + } + + global $wgDevelopmentWarnings; + if ( $wgDevelopmentWarnings ) { + trigger_error( $msg, $level ); } else { - $msg = "Use of $function is deprecated."; + wfDebug( "$msg\n" ); } - wfDebug( "$msg\n" ); } /** @@ -2900,7 +3085,7 @@ function wfDeprecated( $function ) { * 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 + * @param $maxLag Integer * @return null */ function wfWaitForSlaves( $maxLag ) { @@ -2919,8 +3104,42 @@ function wfWaitForSlaves( $maxLag ) { } } +/** + * Output some plain text in command-line mode or in the installer (updaters.inc). + * Do not use it in any other context, its behaviour is subject to change. + */ +function wfOut( $s ) { + static $lineStarted = false; + global $wgCommandLineMode; + if ( $wgCommandLineMode && !defined( 'MEDIAWIKI_INSTALL' ) ) { + echo $s; + } else { + echo htmlspecialchars( $s ); + } + flush(); +} + +/** + * Count down from $n to zero on the terminal, with a one-second pause + * between showing each number. For use in command-line scripts. + */ +function wfCountDown( $n ) { + for ( $i = $n; $i >= 0; $i-- ) { + if ( $i != $n ) { + echo str_repeat( "\x08", strlen( $i + 1 ) ); + } + echo $i; + flush(); + if ( $i ) { + sleep( 1 ); + } + } + echo "\n"; +} + /** Generate a random 32-character hexadecimal token. - * @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing. + * @param $salt Mixed: some sort of salt, if necessary, to add to random + * characters before hashing. */ function wfGenerateToken( $salt = '' ) { $salt = serialize($salt); @@ -2930,10 +3149,121 @@ function wfGenerateToken( $salt = '' ) { /** * Replace all invalid characters with - - * @param mixed $title Filename to process + * @param $name Mixed: filename to process */ function wfStripIllegalFilenameChars( $name ) { $name = wfBaseName( $name ); $name = preg_replace ( "/[^".Title::legalChars()."]|:/", '-', $name ); return $name; } + +/** + * Insert array into another array after the specified *KEY* + * @param $array Array: The array. + * @param $insert Array: The array to insert. + * @param $after Mixed: The key to insert after + */ +function wfArrayInsertAfter( $array, $insert, $after ) { + // Find the offset of the element to insert after. + $keys = array_keys($array); + $offsetByKey = array_flip( $keys ); + + $offset = $offsetByKey[$after]; + + // Insert at the specified offset + $before = array_slice( $array, 0, $offset + 1, true ); + $after = array_slice( $array, $offset + 1, count($array)-$offset, true ); + + $output = $before + $insert + $after; + + return $output; +} + +/* Recursively converts the parameter (an object) to an array with the same data */ +function wfObjectToArray( $object, $recursive = true ) { + $array = array(); + foreach ( get_object_vars($object) as $key => $value ) { + if ( is_object($value) && $recursive ) { + $value = wfObjectToArray( $value ); + } + + $array[$key] = $value; + } + + return $array; +} + +/** + * Set PHP's memory limit to the larger of php.ini or $wgMemoryLimit; + * @return Integer value memory was set to. + */ + +function wfMemoryLimit () { + global $wgMemoryLimit; + $memlimit = wfShorthandToInteger( ini_get( "memory_limit" ) ); + $conflimit = wfShorthandToInteger( $wgMemoryLimit ); + if( $memlimit != -1 ) { + if( $conflimit == -1 ) { + wfDebug( "Removing PHP's memory limit\n" ); + wfSuppressWarnings(); + ini_set( "memory_limit", $conflimit ); + wfRestoreWarnings(); + return $conflimit; + } elseif ( $conflimit > $memlimit ) { + wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" ); + wfSuppressWarnings(); + ini_set( "memory_limit", $conflimit ); + wfRestoreWarnings(); + return $conflimit; + } + } + return $memlimit; +} + +/** + * Converts shorthand byte notation to integer form + * @param $string String + * @return Integer + */ +function wfShorthandToInteger ( $string = '' ) { + $string = trim($string); + if( empty($string) ) { return -1; } + $last = strtolower($string[strlen($string)-1]); + $val = intval($string); + switch($last) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; +} + +/* Get the normalised IETF language tag + * @param $code String: The language code. + * @return $langCode String: The language code which complying with BCP 47 standards. + */ +function wfBCP47( $code ) { + $codeSegment = explode( '-', $code ); + foreach ( $codeSegment as $segNo => $seg ) { + if ( count( $codeSegment ) > 0 ) { + // ISO 3166 country code + if ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) + $codeBCP[$segNo] = strtoupper( $seg ); + // ISO 15924 script code + else if ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) + $codeBCP[$segNo] = ucfirst( $seg ); + // Use lowercase for other cases + else + $codeBCP[$segNo] = strtolower( $seg ); + } else { + // Use lowercase for single segment + $codeBCP[$segNo] = strtolower( $seg ); + } + } + $langCode = implode ( '-' , $codeBCP ); + return $langCode; +}