X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FGlobalFunctions.php;h=7f5f3a82bd329a0f8ad1501464246e7c89abe95f;hb=4b1811dcb088d5eb589a7a43559c03003d3b9f71;hp=0ea76e5955dc783d619a2e4509df3d69bfbd49d8;hpb=d8e90514ab1a80545fad0dbe420a61480b177104;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 0ea76e5955..7f5f3a82bd 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -9,33 +9,26 @@ * 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( 'DatabaseFunctions.php' ); -require_once( 'UpdateClasses.php' ); require_once( 'LogPage.php' ); require_once( 'normal/UtfNormalUtil.php' ); +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,25 +42,10 @@ 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 ) { + $ar = array(); preg_match_all( '/./us', $str, $ar ); if( func_num_args() >= 3 ) { @@ -79,17 +57,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+ @@ -98,7 +65,7 @@ if ( !function_exists( 'array_diff_key' ) ) { */ function array_diff_key( $left, $right ) { $result = $left; - foreach ( $left as $key => $value ) { + foreach ( $left as $key => $unused ) { if ( isset( $right[$key] ) ) { unset( $result[$key] ); } @@ -107,31 +74,27 @@ if ( !function_exists( 'array_diff_key' ) ) { } } -// If it doesn't exist no ctype_* stuff will -if ( ! function_exists( 'ctype_alnum' ) ) - require_once 'compatability/ctype.php'; +/** + * 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 ) { + 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 */ } /** @@ -144,7 +107,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; @@ -154,7 +117,7 @@ function wfRandom() { * We want / and : to be included as literal characters in our title URLs. * %2F in the page titles seems to fatally break for some reason. * - * @param string $s + * @param $s String: * @return string */ function wfUrlencode ( $s ) { @@ -175,18 +138,30 @@ function wfUrlencode ( $s ) { * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output. * $wgDebugComments - if on, some debug items may appear in comments in the HTML output. * - * @param string $text - * @param bool $logonly Set true to avoid appearing in HTML when $wgDebugComments is set + * @param $text String + * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set */ 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 ) { @@ -201,16 +176,18 @@ function wfDebug( $text, $logonly = false ) { * Send a line to a supplementary debug log file, if configured, or main debug log if not. * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log. * - * @param string $logGroup - * @param string $text - * @param bool $public Whether to log the event in the public log if no private + * @param $logGroup String + * @param $text String + * @param $public Bool: whether to log the event in the public log if no private * log file is specified, (default true) */ function wfDebugLog( $logGroup, $text, $public = true ) { - global $wgDebugLogGroups, $wgDBname; + global $wgDebugLogGroups; if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n"; if( isset( $wgDebugLogGroups[$logGroup] ) ) { - @error_log( "$wgDBname: $text", 3, $wgDebugLogGroups[$logGroup] ); + $time = wfTimestamp( TS_DB ); + $wiki = wfWikiID(); + @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] ); } else if ( $public === true ) { wfDebug( $text, true ); } @@ -218,7 +195,7 @@ function wfDebugLog( $logGroup, $text, $public = true ) { /** * Log for database errors - * @param string $text Database error message. + * @param $text String: database error message. */ function wfLogDBError( $text ) { global $wgDBerrorLog; @@ -232,16 +209,13 @@ function wfLogDBError( $text ) { /** * @todo document */ -function logProfilingData() { +function wfLogProfilingData() { global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; global $wgProfiling, $wgUser; - $now = wfTime(); - - list( $usec, $sec ) = explode( ' ', $wgRequestTime ); - $start = (float)$sec + (float)$usec; - $elapsed = $now - $start; if ( $wgProfiling ) { - $prof = wfGetProfilingOutput( $start, $elapsed ); + $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']; @@ -251,7 +225,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, @@ -271,28 +246,26 @@ function logProfilingData() { function wfReadOnly() { global $wgReadOnlyFile, $wgReadOnly; - if ( $wgReadOnly ) { - return true; + if ( !is_null( $wgReadOnly ) ) { + return (bool)$wgReadOnly; } if ( '' == $wgReadOnlyFile ) { return false; } - - // Set $wgReadOnly and unset $wgReadOnlyFile, for faster access next time + // Set $wgReadOnly for faster access next time if ( is_file( $wgReadOnlyFile ) ) { $wgReadOnly = file_get_contents( $wgReadOnlyFile ); } else { $wgReadOnly = false; } - $wgReadOnlyFile = ''; - return $wgReadOnly; + return (bool)$wgReadOnly; } /** * Get a message from anywhere, for the current user language. * - * Use wfMsgForContent() instead if the message should NOT + * Use wfMsgForContent() instead if the message should NOT * change depending on the user preferences. * * Note that the message may contain HTML, and is therefore @@ -300,8 +273,12 @@ function wfReadOnly() { * addWikiText will do the escaping for you. Use wfMsgHtml() * if you need an escaped message. * - * @param string lookup key for the message, usually + * @param $key String: lookup key for the message, usually * defined in languages/Language.php + * + * This function also takes extra optional parameters (not + * shown in the function definition), which can by used to + * insert variable text into the predefined message. */ function wfMsg( $key ) { $args = func_get_args(); @@ -315,29 +292,29 @@ 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 ); } /** * Get a message from anywhere, for the current global language * set with $wgLanguageCode. - * - * Use this if the message should NOT change dependent on the - * language set in the user's preferences. This is the case for - * most text written into logs, as well as link targets (such as - * the name of the copyright policy page). Link titles, on the + * + * Use this if the message should NOT change dependent on the + * language set in the user's preferences. This is the case for + * most text written into logs, as well as link targets (such as + * the name of the copyright policy page). Link titles, on the * other hand, should be shown in the UI language. * - * Note that MediaWiki allows users to change the user interface - * language in their preferences, but a single installation + * Note that MediaWiki allows users to change the user interface + * language in their preferences, but a single installation * typically only contains content in one language. - * - * Be wary of this distinction: If you use wfMsg() where you should - * use wfMsgForContent(), a user of the software may have to + * + * Be wary of this distinction: If you use wfMsg() where you should + * use wfMsgForContent(), a user of the software may have to * customize over 70 messages in order to, e.g., fix a link in every * possible language. * - * @param string lookup key for the message, usually + * @param $key String: lookup key for the message, usually * defined in languages/Language.php */ function wfMsgForContent( $key ) { @@ -391,30 +368,32 @@ function wfMsgNoDBForContent( $key ) { /** * Really get a message - */ -function wfMsgReal( $key, $args, $useDB, $forContent=false, $transform = true ) { - $fname = 'wfMsgReal'; - wfProfileIn( $fname ); - + * @param $key String: key to get. + * @param $args + * @param $useDB Boolean + * @param $transform Boolean: Whether or not to transform the message. + * @param $forContent Boolean + * @return String: the requested message. + */ +function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) { $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); $message = wfMsgReplaceArgs( $message, $args ); - wfProfileOut( $fname ); return $message; } /** - * This function provides the message source for messages to be edited which are *not* stored in the database -*/ - + * This function provides the message source for messages to be edited which are *not* stored in the database. + * @param $key String: + */ function wfMsgWeirdKey ( $key ) { $subsource = str_replace ( ' ' , '_' , $key ) ; - $source = wfMsg ( $subsource ) ; - if ( $source == "<{$subsource}>" ) { + $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 == "<{$subsource}>" ) { + if ( wfEmptyMsg( $subsource, $source ) ) { # Didn't work either, return blank text $source = "" ; } @@ -427,14 +406,14 @@ function wfMsgWeirdKey ( $key ) { * @param bool $useDB * @param bool $forContent * @return string - * @access private + * @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(); - + if( is_object( $wgMessageCache ) ) { if ( ! $transform ) $wgMessageCache->disableTransform(); @@ -457,13 +436,13 @@ 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() ); } } if ( is_object( $wgMessageCache ) && ! $transform ) $wgMessageCache->setTransform( $transstat ); - + return $message; } @@ -473,7 +452,7 @@ function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) { * @param string $message * @param array $args * @return string - * @access private + * @private */ function wfMsgReplaceArgs( $message, $args ) { # Fix windows line-endings @@ -481,16 +460,19 @@ function wfMsgReplaceArgs( $message, $args ) { $message = str_replace( "\r", '', $message ); // Replace arguments - if ( count( $args ) ) - if ( is_array( $args[0] ) ) - foreach ( $args[0] as $key => $val ) + 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 ); + } + } else { + foreach( $args as $n => $param ) { + $replacementKeys['$' . ($n + 1)] = $param; + } + $message = strtr( $message, $replacementKeys ); + } } - + return $message; } @@ -529,15 +511,73 @@ function wfMsgWikiHtml( $key ) { return wfMsgReplaceArgs( $wgOut->parse( wfMsgGetKey( $key, true ), /* can't be set to false */ true ), $args ); } +/** + * Returns message in the requested format + * @param string $key Key of the message + * @param array $options Processing rules: + * parse: parses wikitext to html + * parseinline: parses wikitext to html and removes the surrounding p's added by parser or tidy + * escape: filters message trough htmlspecialchars + * replaceafter: parameters are substituted after parsing or escaping + * parsemag: ?? + */ +function wfMsgExt( $key, $options ) { + global $wgOut, $wgParser; + + $args = func_get_args(); + array_shift( $args ); + array_shift( $args ); + + if( !is_array($options) ) { + $options = array($options); + } + + $string = wfMsgGetKey( $key, true, false, false ); + + if( !in_array('replaceafter', $options) ) { + $string = wfMsgReplaceArgs( $string, $args ); + } + + if( in_array('parse', $options) ) { + $string = $wgOut->parse( $string, true, true ); + } elseif ( in_array('parseinline', $options) ) { + $string = $wgOut->parse( $string, true, true ); + $m = array(); + if( preg_match( "~^

(.*)\n?

$~", $string, $m ) ) { + $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 ); + } + + if ( in_array('escape', $options) ) { + $string = htmlspecialchars ( $string ); + } + + if( in_array('replaceafter', $options) ) { + $string = wfMsgReplaceArgs( $string, $args ); + } + + return $string; +} + + /** * 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 */ function wfAbruptExit( $error = false ){ global $wgLoadBalancer; static $called = false; if ( $called ){ - exit(); + exit( -1 ); } $called = true; @@ -552,43 +592,60 @@ function wfAbruptExit( $error = false ){ wfDebug('WARNING: Abrupt exit\n'); } - wfProfileClose(); - logProfilingData(); + wfLogProfilingData(); if ( !$error ) { $wgLoadBalancer->closeAll(); } - exit(); + exit( -1 ); } /** - * @todo document + * @obsolete Please return control the caller or throw an exception */ function wfErrorExit() { wfAbruptExit( true ); } /** - * Die with a backtrace - * This is meant as a debugging aid to track down where bad data comes from. - * Shouldn't be used in production code except maybe in "shouldn't happen" areas. + * 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 + */ +function wfDie( $msg='' ) { + echo $msg; + die( 1 ); +} + +/** + * 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. */ function wfDebugDieBacktrace( $msg = '' ) { - global $wgCommandLineMode; + throw new MWException( $msg ); +} - $backtrace = wfBacktrace(); - if ( $backtrace !== false ) { - if ( $wgCommandLineMode ) { - $msg .= "\nBacktrace:\n$backtrace"; - } else { - $msg .= "\n

Backtrace:

\n$backtrace"; - } +/** + * Fetch server name for use in error reporting etc. + * Use real server name if available, so we know which machine + * in a server farm generated the current page. + * @return string + */ +function wfHostname() { + if ( function_exists( 'posix_uname' ) ) { + // This function not present on Windows + $uname = @posix_uname(); + } else { + $uname = false; + } + if( is_array( $uname ) && isset( $uname['nodename'] ) ) { + return $uname['nodename']; + } else { + # This may be a virtual server. + return $_SERVER['SERVER_NAME']; } - echo $msg; - echo wfReportTime()."\n"; - die( -1 ); } /** @@ -600,25 +657,10 @@ function wfDebugDieBacktrace( $msg = '' ) { global $wgRequestTime; $now = wfTime(); - list( $usec, $sec ) = explode( ' ', $wgRequestTime ); - $start = (float)$sec + (float)$usec; - $elapsed = $now - $start; - - # Use real server name if available, so we know which machine - # in a server farm generated the current page. - if ( function_exists( 'posix_uname' ) ) { - $uname = @posix_uname(); - } else { - $uname = false; - } - if( is_array( $uname ) && isset( $uname['nodename'] ) ) { - $hostname = $uname['nodename']; - } else { - # This may be a virtual server. - $hostname = $_SERVER['SERVER_NAME']; - } - $com = sprintf( "", - $hostname, $elapsed ); + $elapsed = $now - $wgRequestTime; + + $com = sprintf( "", + wfHostname(), $elapsed ); return $com; } @@ -693,7 +735,7 @@ function wfShowingResultsNum( $offset, $limit, $num ) { * @todo document */ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { - global $wgUser, $wgLang; + global $wgLang; $fmtLimit = $wgLang->formatNum( $limit ); $prev = wfMsg( 'prevn', $fmtLimit ); $next = wfMsg( 'nextn', $fmtLimit ); @@ -707,7 +749,6 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { } } - $sk = $wgUser->getSkin(); if ( 0 != $offset ) { $po = $offset - $limit; if ( $po < 0 ) { $po = 0; } @@ -738,7 +779,7 @@ function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { * @todo document */ function wfNumLink( $offset, $limit, &$title, $query = '' ) { - global $wgUser, $wgLang; + global $wgLang; if ( '' == $query ) { $q = ''; } else { $q = $query.'&'; } $q .= 'limit='.$limit.'&offset='.$offset; @@ -758,6 +799,7 @@ function wfClientAcceptsGzip() { global $wgUseGzip; if( $wgUseGzip ) { # FIXME: we may want to blacklist some broken browsers + $m = array(); if( preg_match( '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/', $_SERVER['HTTP_ACCEPT_ENCODING'], @@ -771,7 +813,13 @@ function wfClientAcceptsGzip() { } /** - * Yay, more global functions! + * Obtain the offset and limit values from the request string; + * used in special pages + * + * @param $deflimit Default limit if none supplied + * @param $optionname Name of a user preference to check against + * @return array + * */ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { global $wgRequest; @@ -790,8 +838,8 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { */ function wfEscapeWikiText( $text ) { $text = str_replace( - array( '[', '|', '\'', 'ISBN ' , '://' , "\n=", '{{' ), - array( '[', '|', ''', 'ISBN ', '://' , "\n=", '{{' ), + array( '[', '|', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), + array( '[', '|', ''', 'ISBN ', 'RFC ', '://', "\n=", '{{' ), htmlspecialchars($text) ); return $text; } @@ -817,46 +865,13 @@ function wfQuotedPrintable( $string, $charset = '' ) { return $out; } -/** - * Returns an escaped string suitable for inclusion in a string literal - * for JavaScript source code. - * Illegal control characters are assumed not to be present. - * - * @param string $string - * @return string - */ -function wfEscapeJsString( $string ) { - // See ECMA 262 section 7.8.4 for string literal format - $pairs = array( - "\\" => "\\\\", - "\"" => "\\\"", - '\'' => '\\\'', - "\n" => "\\n", - "\r" => "\\r", - - # To avoid closing the element or CDATA section - "<" => "\\x3c", - ">" => "\\x3e", - ); - return strtr( $string, $pairs ); -} /** * @todo document * @return float */ function wfTime() { - $st = explode( ' ', microtime() ); - return (float)$st[0] + (float)$st[1]; -} - -/** - * Changes the first character to an HTML entity - */ -function wfHtmlEscapeFirst( $text ) { - $ord = ord($text); - $newText = substr($text, 1); - return "&#$ord;$newText"; + return microtime(true); } /** @@ -934,7 +949,29 @@ function wfEscapeShellArg( ) { } if ( wfIsWindows() ) { - $retVal .= '"' . str_replace( '"','\"', $arg ) . '"'; + // Escaping for an MSVC-style command line parser + // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html + // Double the backslashes before any double quotes. Escape the double quotes. + $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); + $arg = ''; + $delim = false; + foreach ( $tokens as $token ) { + if ( $delim ) { + $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; + } else { + $arg .= $token; + } + $delim = !$delim; + } + // Double the backslashes before the end of the string, because + // we will soon add a quote + $m = array(); + if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { + $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); + } + + // Add surrounding quotes + $retVal .= '"' . $arg . '"'; } else { $retVal .= escapeshellarg( $arg ); } @@ -1035,6 +1072,64 @@ function wfHttpError( $code, $label, $desc ) { "

\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. + * + * @parameter bool $resetGzipEncoding + */ +function wfResetOutputBuffers( $resetGzipEncoding=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 @@ -1052,6 +1147,7 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) { foreach( $parts as $part ) { # FIXME: doesn't deal with params like 'text/html; level=1' @list( $value, $qpart ) = explode( ';', $part ); + $match = array(); if( !isset( $qpart ) ) { $prefs[$value] = 1; } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) { @@ -1072,7 +1168,7 @@ function wfAcceptToPrefs( $accept, $def = '*/*' ) { * @param string $type * @param array $avail * @return string - * @access private + * @private */ function mimeTypeMatch( $type, $avail ) { if( array_key_exists($type, $avail) ) { @@ -1212,14 +1308,14 @@ define('TS_RFC2822', 3); /** * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z * - * This is used by Special:Export + * This is used by Special:Export */ define('TS_ISO_8601', 4); /** * An Exif timestamp (YYYY:MM:DD HH:MM:SS) * - * @link http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the + * @url 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. */ @@ -1230,31 +1326,37 @@ 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 - and act accordingly. + * and act accordingly. * @return string Time in the format specified in $outputtype */ function wfTimestamp($outputtype=TS_UNIX,$ts=0) { $uts = 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,$da)) { # 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", @@ -1263,6 +1365,14 @@ 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]); + } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/',$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"); @@ -1286,8 +1396,10 @@ 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: - wfDebugDieBacktrace( 'wfTimestamp() called with illegal output type.'); + throw new MWException( 'wfTimestamp() called with illegal output type.'); } } @@ -1328,174 +1440,112 @@ function swap( &$x, &$y ) { $y = $z; } -function wfGetSiteNotice() { - global $wgSiteNotice, $wgTitle, $wgOut, $parserMemc, $wgDBname; - $fname = 'wfGetSiteNotice'; +function wfGetCachedNotice( $name ) { + global $wgOut, $parserMemc; + $fname = 'wfGetCachedNotice'; wfProfileIn( $fname ); - - $shouldParse=false; - - $notice = wfMsgForContent( 'sitenotice' ); - if( $notice == '<sitenotice>' || $notice == '-' ) { - $notice = ''; - } - if( $notice == '' ) { - # We may also need to override a message with eg downtime info - # FIXME: make this work! + + $needParse = 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 ); + } } - if($notice != '-' && $notice != '') { - $cachednotice=$parserMemc->get("{$wgDBname}:sitenotice"); - if (is_array($cachednotice)) { - if (md5($notice)==$cachednotice['hash']) { - $notice = $cachednotice['html']; - } else { - $shouldParse=true; - } + + $cachedNotice = $parserMemc->get( wfMemcKey( $name ) ); + if( is_array( $cachedNotice ) ) { + if( md5( $notice ) == $cachedNotice['hash'] ) { + $notice = $cachedNotice['html']; } else { - $shouldParse=true; + $needParse = true; } - if ($shouldParse) { - if( is_object( $wgOut ) ) { - $parsed = $wgOut->parse( $notice ); - $parserMemc->set("{$wgDBname}:sitenotice", - array('html' => $parsed, 'hash' => md5($notice)), 600); - $notice = $parsed; - } else { - wfDebug( "wfGetSiteNotice called with no \$wgOut available" ); - $notice = ''; - } + } else { + $needParse = true; + } + + if( $needParse ) { + if( is_object( $wgOut ) ) { + $parsed = $wgOut->parse( $notice ); + $parserMemc->set( wfMemcKey( $name ), array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); + $notice = $parsed; + } else { + wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' ); + $notice = ''; } } + wfProfileOut( $fname ); return $notice; } -/** - * Format an XML element with given attributes and, optionally, text content. - * Element and attribute names are assumed to be ready for literal inclusion. - * Strings are assumed to not contain XML-illegal characters; special - * characters (<, >, &) are escaped but illegals are not touched. - * - * @param string $element - * @param array $attribs Name=>value pairs. Values will be escaped. - * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) - * @return string - */ -function wfElement( $element, $attribs = null, $contents = '') { - $out = '<' . $element; - if( !is_null( $attribs ) ) { - foreach( $attribs as $name => $val ) { - $out .= ' ' . $name . '="' . htmlspecialchars( $val ) . '"'; - } - } - if( is_null( $contents ) ) { - $out .= '>'; +function wfGetNamespaceNotice() { + global $wgTitle; + + # Paranoia + if ( !isset( $wgTitle ) || !is_object( $wgTitle ) ) + return ""; + + $fname = 'wfGetNamespaceNotice'; + wfProfileIn( $fname ); + + $key = "namespacenotice-" . $wgTitle->getNsText(); + $namespaceNotice = wfGetCachedNotice( $key ); + if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "

<" ) { + $namespaceNotice = '

' . $namespaceNotice . "
"; } else { - if( $contents == '' ) { - $out .= ' />'; - } else { - $out .= '>' . htmlspecialchars( $contents ) . ""; - } + $namespaceNotice = ""; } - return $out; -} -/** - * Format an XML element as with wfElement(), but run text through the - * UtfNormal::cleanUp() validator first to ensure that no invalid UTF-8 - * is passed. - * - * @param string $element - * @param array $attribs Name=>value pairs. Values will be escaped. - * @param string $contents NULL to make an open tag only; '' for a contentless closed tag (default) - * @return string - */ -function wfElementClean( $element, $attribs = array(), $contents = '') { - if( $attribs ) { - $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs ); - } - if( $contents ) { - $contents = UtfNormal::cleanUp( $contents ); - } - return wfElement( $element, $attribs, $contents ); + wfProfileOut( $fname ); + return $namespaceNotice; } -// Shortcuts -function wfOpenElement( $element, $attribs = null ) { return wfElement( $element, $attribs, null ); } -function wfCloseElement( $element ) { return ""; } - -/** - * Create a namespace selector - * - * @param mixed $selected The namespace which should be selected, default '' - * @param string $allnamespaces Value of a special item denoting all namespaces. Null to not include (default) - * @return Html string containing the namespace selector - */ -function &HTMLnamespaceselector($selected = '', $allnamespaces = null) { - global $wgContLang; - if( $selected !== '' ) { - if( is_null( $selected ) ) { - // No namespace selected; let exact match work without hitting Main - $selected = ''; +function wfGetSiteNotice() { + global $wgUser, $wgSiteNotice; + $fname = 'wfGetSiteNotice'; + wfProfileIn( $fname ); + $siteNotice = ''; + + if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) { + if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) { + $siteNotice = wfGetCachedNotice( 'sitenotice' ); } else { - // Let input be numeric strings without breaking the empty match. - $selected = intval( $selected ); + $anonNotice = wfGetCachedNotice( 'anonnotice' ); + if( !$anonNotice ) { + $siteNotice = wfGetCachedNotice( 'sitenotice' ); + } else { + $siteNotice = $anonNotice; + } } - } - $s = "\n"; - return $s; -} -/** Global singleton instance of MimeMagic. This is initialized on demand, -* please always use the wfGetMimeMagic() function to get the instance. -* -* @access private -*/ -$wgMimeMagic= NULL; + wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) ); + wfProfileOut( $fname ); + return $siteNotice; +} -/** Factory functions for the global MimeMagic object. -* This function always returns the same singleton instance of MimeMagic. -* That objects will be instantiated on the first call to this function. -* If needed, the MimeMagic.php file is automatically included by this function. -* @return MimeMagic the global MimeMagic objects. -*/ +/** + * BC wrapper for MimeMagic::singleton() + * @deprecated + */ function &wfGetMimeMagic() { - global $wgMimeMagic; - - if (!is_null($wgMimeMagic)) { - return $wgMimeMagic; - } - - if (!class_exists("MimeMagic")) { - #include on demand - require_once("MimeMagic.php"); - } - - $wgMimeMagic= new MimeMagic(); - - return $wgMimeMagic; + return MimeMagic::singleton(); } - /** * Tries to get the system directory for temporary files. * The TMPDIR, TMP, and TEMP environment variables are checked in sequence, @@ -1520,31 +1570,59 @@ function wfTempDir() { /** * Make directory, and make all parent directories if they don't exist */ -function wfMkdirParents( $fullDir, $mode ) { - $parts = explode( '/', $fullDir ); - $path = ''; +function wfMkdirParents( $fullDir, $mode = 0777 ) { + if ( strval( $fullDir ) === '' ) { + return true; + } - foreach ( $parts as $dir ) { - $path .= $dir . '/'; - if ( !is_dir( $path ) ) { - if ( !mkdir( $path, $mode ) ) { - return false; - } + # 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; } /** * Increment a statistics counter */ -function wfIncrStats( $key ) { - global $wgDBname, $wgMemc; - $key = "$wgDBname:stats:$key"; - if ( is_null( $wgMemc->incr( $key ) ) ) { - $wgMemc->add( $key, 1 ); - } -} + function wfIncrStats( $key ) { + global $wgMemc; + $key = wfMemcKey( 'stats', $key ); + if ( is_null( $wgMemc->incr( $key ) ) ) { + $wgMemc->add( $key, 1 ); + } + } /** * @param mixed $nr The number to format @@ -1579,7 +1657,7 @@ function wfEncryptPassword( $userid, $password ) { */ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { if ( is_null( $changed ) ) { - wfDebugDieBacktrace('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); + throw new MWException('GlobalFunctions::wfAppendToArrayIfNotDefault got null'); } if ( $default[$key] !== $value ) { $changed[$key] = $value; @@ -1610,6 +1688,12 @@ function in_string( $needle, $str ) { return strpos( $str, $needle ) !== false; } +function wfSpecialList( $page, $details ) { + global $wgContLang; + $details = $details ? ' ' . $wgContLang->getDirMark() . "($details)" : ""; + return $page . $details; +} + /** * Returns a regular expression of url protocols * @@ -1618,81 +1702,59 @@ function in_string( $needle, $str ) { function wfUrlProtocols() { global $wgUrlProtocols; - $protocols = array(); - foreach ($wgUrlProtocols as $protocol) - $protocols[] = preg_quote( $protocol, '/' ); - - return implode( '|', $protocols ); -} + // Support old-style $wgUrlProtocols strings, for backwards compatibility + // with LocalSettings files from 1.5 + if ( is_array( $wgUrlProtocols ) ) { + $protocols = array(); + foreach ($wgUrlProtocols as $protocol) + $protocols[] = preg_quote( $protocol, '/' ); -/** - * Check if a string is well-formed XML. - * Must include the surrounding tag. - * - * @param string $text - * @return bool - * - * @todo Error position reporting return - */ -function wfIsWellFormedXml( $text ) { - $parser = xml_parser_create( "UTF-8" ); - - # case folding violates XML standard, turn it off - xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false ); - - if( !xml_parse( $parser, $text, true ) ) { - $err = xml_error_string( xml_get_error_code( $parser ) ); - $position = xml_get_current_byte_index( $parser ); - //$fragment = $this->extractFragment( $html, $position ); - //$this->mXmlError = "$err at byte $position:\n$fragment"; - xml_parser_free( $parser ); - return false; + return implode( '|', $protocols ); + } else { + return $wgUrlProtocols; } - xml_parser_free( $parser ); - return true; -} - -/** - * Check if a string is a well-formed XML fragment. - * Wraps fragment in an bit and doctype, so it can be a fragment - * and can use HTML named entities. - * - * @param string $text - * @return bool - */ -function wfIsWellFormedXmlFragment( $text ) { - $html = - '' . - '' . - $text . - ''; - return wfIsWellFormedXml( $html ); } /** - * shell_exec() with time and memory limits mirrored from the PHP configuration, - * if supported. + * Execute a shell command, with time and memory limits mirrored from the PHP + * configuration if supported. + * @param $cmd Command line, properly escaped for shell. + * @param &$retval optional, will receive the program's exit code. + * (non-zero is usually failure) + * @return collected stdout as a string (trailing newlines stripped) */ -function wfShellExec( $cmd ) -{ - global $IP; +function wfShellExec( $cmd, &$retval=null ) { + global $IP, $wgMaxShellMemory, $wgMaxShellFileSize; + if( ini_get( 'safe_mode' ) ) { + wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); + $retval = 1; + return "Unable to run external programs in safe mode."; + } + 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 ); + $filesize = intval( $wgMaxShellFileSize ); + if ( $time > 0 && $mem > 0 ) { - $script = "$IP/bin/ulimit.sh"; + $script = "$IP/bin/ulimit-tvf.sh"; if ( is_executable( $script ) ) { - $memKB = intval( $mem / 1024 ); - $cmd = escapeshellarg( $script ) . " $time $memKB $cmd"; + $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd"; } } + } 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 . '"'; } - return shell_exec( $cmd ); + 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 ); + } /** @@ -1712,9 +1774,9 @@ function wfShellExec( $cmd ) */ function wfUsePHP( $req_ver ) { $php_ver = PHP_VERSION; - + if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) - wfDebugDieBacktrace( "PHP $req_ver required--this is only $php_ver" ); + throw new MWException( "PHP $req_ver required--this is only $php_ver" ); } /** @@ -1734,20 +1796,325 @@ function wfUseMW( $req_ver ) { global $wgVersion; if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) - wfDebugDieBacktrace( "MediaWiki $req_ver required--this is only $wgVersion" ); + throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" ); } /** - * Escape a string to make it suitable for inclusion in a preg_replace() - * replacement parameter. + * @deprecated use StringUtils::escapeRegexReplacement + */ +function wfRegexReplacement( $string ) { + return StringUtils::escapeRegexReplacement( $string ); +} + +/** + * Return the final portion of a pathname. + * Reimplemented because PHP5's basename() is buggy with multibyte text. + * http://bugs.php.net/bug.php?id=33898 * - * @param string $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 * @return string */ -function wfRegexReplacement( $string ) { - $string = str_replace( '\\', '\\\\', $string ); - $string = str_replace( '$', '\\$', $string ); - return $string; +function wfBaseName( $path ) { + $matches = array(); + if( preg_match( '#([^/\\\\]*)[/\\\\]*$#', $path, $matches ) ) { + return $matches[1]; + } else { + return ''; + } } +/** + * Make a URL index, appropriate for the el_index field of externallinks. + */ +function wfMakeUrlIndex( $url ) { + wfSuppressWarnings(); + $bits = parse_url( $url ); + wfRestoreWarnings(); + if ( !$bits || $bits['scheme'] !== 'http' ) { + return false; + } + // Reverse the labels in the hostname, convert to lower case + $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"; + // Leave out user and password. Add the port, path, query and fragment + if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port']; + if ( isset( $bits['path'] ) ) { + $index .= $bits['path']; + } else { + $index .= '/'; + } + if ( isset( $bits['query'] ) ) $index .= '?' . $bits['query']; + if ( isset( $bits['fragment'] ) ) $index .= '#' . $bits['fragment']; + return $index; +} + +/** + * Do any deferred updates and clear the list + * TODO: This could be in Wiki.php if that class made any sense at all + */ +function wfDoUpdates() +{ + global $wgPostCommitUpdateList, $wgDeferredUpdateList; + foreach ( $wgDeferredUpdateList as $update ) { + $update->doUpdate(); + } + foreach ( $wgPostCommitUpdateList as $update ) { + $update->doUpdate(); + } + $wgDeferredUpdateList = array(); + $wgPostCommitUpdateList = array(); +} + +/** + * @deprecated use StringUtils::explodeMarkup + */ +function wfExplodeMarkup( $separator, $text ) { + return StringUtils::explodeMarkup( $separator, $text ); +} + +/** + * 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()))); +} + +/** + * Get a cache key + */ +function wfMemcKey( /*... */ ) { + global $wgDBprefix, $wgDBname; + $args = func_get_args(); + if ( $wgDBprefix ) { + $key = "$wgDBname-$wgDBprefix:" . implode( ':', $args ); + } else { + $key = $wgDBname . ':' . implode( ':', $args ); + } + return $key; +} + +/** + * Get a cache key for a foreign DB + */ +function wfForeignMemcKey( $db, $prefix /*, ... */ ) { + $args = array_slice( func_get_args(), 2 ); + if ( $prefix ) { + $key = "$db-$prefix:" . implode( ':', $args ); + } else { + $key = $db . ':' . implode( ':', $args ); + } + return $key; +} + +/** + * Get an ASCII string identifying this wiki + * This is used as a prefix in memcached keys + */ +function wfWikiID() { + global $wgDBprefix, $wgDBname; + if ( $wgDBprefix ) { + return "$wgDBname-$wgDBprefix"; + } else { + return $wgDBname; + } +} + +/* + * Get a Database object + * @param integer $db Index of the connection to get. May be DB_MASTER for the + * master (for write queries), DB_SLAVE for potentially lagged + * read queries, or an integer >= 0 for a particular server. + * + * @param array $groups Query groups. A list of group names that this query + * belongs to. + */ +function &wfGetDB( $db = DB_LAST, $groups = array() ) { + global $wgLoadBalancer; + $ret = $wgLoadBalancer->getConnection( $db, true, $groups ); + return $ret; +} ?>