= 3 ) { $end = func_get_arg( 2 ); return join( '', array_slice( $ar[0], $start, $end ) ); } else { return join( '', array_slice( $ar[0], $start ) ); } } } /** * html_entity_decode exists in PHP 4.3.0+ but is FATALLY BROKEN even then, * with no UTF-8 support. * * @param string $string String having html entities * @param $quote_style * @param string $charset Encoding set to use (default 'ISO-8859-1') */ function do_html_entity_decode( $string, $quote_style=ENT_COMPAT, $charset='ISO-8859-1' ) { static $trans; static $savedCharset; if( !isset( $trans ) || $savedCharset != $charset ) { $trans = array_flip( get_html_translation_table( HTML_ENTITIES, $quote_style ) ); $savedCharset = $charset; # Assumes $charset will always be the same through a run, and only understands # utf-8 or default. Note - mixing latin1 named entities and unicode numbered # ones will result in a bad link. if( strcasecmp( 'utf-8', $charset ) == 0 ) { $trans = array_map( 'utf8_encode', $trans ); } } return strtr( $string, $trans ); } /** * Where as we got a random seed * @var bool $wgTotalViews */ $wgRandomSeeded = false; /** * Seed Mersenne Twister * Only necessary in PHP < 4.2.0 * * @return bool */ 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; } } /** * Get a random decimal value between 0 and 1, in a way * not likely to give duplicate values for any realistic * number of articles. * * @return string */ 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(); $rand = number_format( mt_rand() * mt_rand() / $max / $max, 12, '.', '' ); return $rand; } /** * 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 * @return string */ function wfUrlencode ( $s ) { $s = urlencode( $s ); $s = preg_replace( '/%3[Aa]/', ':', $s ); $s = preg_replace( '/%2[Ff]/', '/', $s ); return $s; } /** * Return the UTF-8 sequence for a given Unicode code point. * Currently doesn't work for values outside the Basic Multilingual Plane. * * @param string $codepoint UTF-8 code point. * @return string HTML UTF-8 Entitie such as 'Ӓ'. */ function wfUtf8Sequence( $codepoint ) { if($codepoint < 0x80) return chr($codepoint); if($codepoint < 0x800) return chr($codepoint >> 6 & 0x3f | 0xc0) . chr($codepoint & 0x3f | 0x80); if($codepoint < 0x10000) return chr($codepoint >> 12 & 0x0f | 0xe0) . chr($codepoint >> 6 & 0x3f | 0x80) . chr($codepoint & 0x3f | 0x80); if($codepoint < 0x110000) return chr($codepoint >> 18 & 0x07 | 0xf0) . chr($codepoint >> 12 & 0x3f | 0x80) . chr($codepoint >> 6 & 0x3f | 0x80) . chr($codepoint & 0x3f | 0x80); # There should be no assigned code points outside this range, but... return "&#$codepoint;"; } /** * Converts numeric character entities to UTF-8 * * @param string $string String to convert. * @return string Converted string. */ function wfMungeToUtf8( $string ) { global $wgInputEncoding; # This is debatable #$string = iconv($wgInputEncoding, "UTF-8", $string); $string = preg_replace ( '/&#([0-9]+);/e', 'wfUtf8Sequence($1)', $string ); $string = preg_replace ( '/&#x([0-9a-f]+);/ie', 'wfUtf8Sequence(0x$1)', $string ); # Should also do named entities here return $string; } /** * Converts a single UTF-8 character into the corresponding HTML character * entity (for use with preg_replace_callback) * * @param array $matches * */ function wfUtf8Entity( $matches ) { $codepoint = utf8ToCodepoint( $matches[0] ); return "&#$codepoint;"; } /** * Converts all multi-byte characters in a UTF-8 string into the appropriate * character entity */ function wfUtf8ToHTML($string) { return preg_replace_callback( '/[\\xc0-\\xfd][\\x80-\\xbf]*/', 'wfUtf8Entity', $string ); } /** * Sends a line to the debug log if enabled or, optionally, to a comment in output. * In normal operation this is a NOP. * * Controlling globals: * $wgDebugLogFile - points to the log file * $wgProfileOnly - if set, normal debug messages will not be recorded. * $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 */ function wfDebug( $text, $logonly = false ) { global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; # 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 ) { $wgOut->debug( $text ); } if ( '' != $wgDebugLogFile && !$wgProfileOnly ) { error_log( $text, 3, $wgDebugLogFile ); } } /** * Log for database errors * @param string $text Database error message. */ function wfLogDBError( $text ) { global $wgDBerrorLog; if ( $wgDBerrorLog ) { $text = date('D M j G:i:s T Y') . "\t".$text; error_log( $text, 3, $wgDBerrorLog ); } } /** * @todo document */ function logProfilingData() { global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest; global $wgProfiling, $wgProfileStack, $wgProfileLimit, $wgUser; $now = wfTime(); list( $usec, $sec ) = explode( ' ', $wgRequestTime ); $start = (float)$sec + (float)$usec; $elapsed = $now - $start; if ( $wgProfiling ) { $prof = wfGetProfilingOutput( $start, $elapsed ); $forward = ''; if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR']; if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP']; if( !empty( $_SERVER['HTTP_FROM'] ) ) $forward .= ' from ' . $_SERVER['HTTP_FROM']; if( $forward ) $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; if($wgUser->getId() == 0) $forward .= ' anon'; $log = sprintf( "%s\t%04.3f\t%s\n", gmdate( 'YmdHis' ), $elapsed, urldecode( $_SERVER['REQUEST_URI'] . $forward ) ); if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) { error_log( $log . $prof, 3, $wgDebugLogFile ); } } } /** * Check if the wiki read-only lock file is present. This can be used to lock * off editing functions, but doesn't guarantee that the database will not be * modified. * @return bool */ function wfReadOnly() { global $wgReadOnlyFile; if ( '' == $wgReadOnlyFile ) { return false; } return is_file( $wgReadOnlyFile ); } /** * Get a message from anywhere, for the UI elements */ function wfMsg( $key ) { $args = func_get_args(); if ( count( $args ) ) { array_shift( $args ); } return wfMsgReal( $key, $args, true ); } /** * Get a message from anywhere, for the content */ function wfMsgForContent( $key ) { $args = func_get_args(); if ( count( $args ) ) { array_shift( $args ); } return wfMsgReal( $key, $args, true, true ); } /** * Get a message from the language file, for the UI elements */ function wfMsgNoDB( $key ) { $args = func_get_args(); if ( count( $args ) ) { array_shift( $args ); } return wfMsgReal( $key, $args, false ); } /** * Get a message from the language file, for the content */ function wfMsgNoDBForContent( $key ) { $args = func_get_args(); if ( count( $args ) ) { array_shift( $args ); } return wfMsgReal( $key, $args, false, true ); } /** * Really get a message */ function wfMsgReal( $key, $args, $useDB, $forContent=false ) { static $replacementKeys = array( '$1', '$2', '$3', '$4', '$5', '$6', '$7', '$8', '$9' ); global $wgParser, $wgMsgParserOptions; global $wgContLang, $wgLanguageCode; $fname = 'wfMsgReal'; wfProfileIn( $fname ); if( $forContent ) { /** * Message is needed for page content, and needs * to be consistent with the site's configured * language. It might be part of a page title, * or a link, or text that will go into the * parser cache and be served back to other * visitors. */ global $wgMessageCache; $cache = &$wgMessageCache; $lang = &$wgContLang; } else { /** * Message is for display purposes only. * The user may have selected a conversion-based * language variant or a separate user interface * language; if so use that. */ if( in_array( $wgLanguageCode, $wgContLang->getVariants() ) ) { global $wgLang, $wgMessageCache; $cache = &$wgMessageCache; $lang = &$wgLang; } else { global $wgLang; $cache = false; $lang = &$wgLang; } } if( is_object( $cache ) ) { $message = $cache->get( $key, $useDB, $forContent ); } elseif( is_object( $lang ) ) { wfSuppressWarnings(); $message = $lang->getMessage( $key ); wfRestoreWarnings(); if(!$message) $message = Language::getMessage($key); if(strstr($message, '{{' ) !== false) { $message = $wgParser->transformMsg($message, $wgMsgParserOptions); } } else { wfDebug( "No language object when getting $key\n" ); $message = "<$key>"; } # Replace arguments if( count( $args ) ) { $message = str_replace( $replacementKeys, $args, $message ); } wfProfileOut( $fname ); return $message; } /** * Just like exit() but makes a note of it. * Commits open transactions except if the error parameter is set */ function wfAbruptExit( $error = false ){ global $wgLoadBalancer; static $called = false; if ( $called ){ exit(); } $called = true; if( function_exists( 'debug_backtrace' ) ){ // PHP >= 4.3 $bt = debug_backtrace(); for($i = 0; $i < count($bt) ; $i++){ $file = $bt[$i]['file']; $line = $bt[$i]['line']; wfDebug("WARNING: Abrupt exit in $file at line $line\n"); } } else { wfDebug('WARNING: Abrupt exit\n'); } if ( !$error ) { $wgLoadBalancer->closeAll(); } exit(); } /** * @todo document */ 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. * * @param string $msg Message shown when dieing. */ function wfDebugDieBacktrace( $msg = '' ) { global $wgCommandLineMode; if ( function_exists( 'debug_backtrace' ) ) { if ( $wgCommandLineMode ) { $msg .= "\nBacktrace:\n"; } else { $msg .= "\n

Backtrace:

\n