X-Git-Url: http://git.cyclocoop.org/?a=blobdiff_plain;f=includes%2FGlobalFunctions.php;h=18d08ea2aa80055c07b30227bf9071d9d630fb51;hb=b066397f10008e30242afa3eeb0af692502e419e;hp=80b15a3eedea386dcf54b4a7808d5c2a635683c2;hpb=7dd27a858cc4c455b466625fca0fe7fe44220912;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php index 80b15a3eed..18d08ea2aa 100644 --- a/includes/GlobalFunctions.php +++ b/includes/GlobalFunctions.php @@ -20,176 +20,58 @@ require_once dirname( __FILE__ ) . '/normal/UtfNormalUtil.php'; * 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. - # Assume will only ever use utf-8 and iso-8859-1. - # This will *not* work in all circumstances. + /** @codeCoverageIgnore */ function iconv( $from, $to, $string ) { - if ( substr( $to, -8 ) == '//IGNORE' ) { - $to = substr( $to, 0, strlen( $to ) - 8 ); - } - if( strcasecmp( $from, $to ) == 0 ) { - return $string; - } - if( strcasecmp( $from, 'utf-8' ) == 0 ) { - return utf8_decode( $string ); - } - if( strcasecmp( $to, 'utf-8' ) == 0 ) { - return utf8_encode( $string ); - } - return $string; + return Fallback::iconv( $from, $to, $string ); } } if ( !function_exists( 'mb_substr' ) ) { - /** - * Fallback implementation for mb_substr, hardcoded to UTF-8. - * Attempts to be at least _moderately_ efficient; best optimized - * for relatively small offset and count values -- about 5x slower - * than native mb_string in my testing. - * - * Larger offsets are still fairly efficient for Latin text, but - * can be up to 100x slower than native if the text is heavily - * multibyte and we have to slog through a few hundred kb. - */ + /** @codeCoverageIgnore */ function mb_substr( $str, $start, $count='end' ) { - if( $start != 0 ) { - $split = mb_substr_split_unicode( $str, intval( $start ) ); - $str = substr( $str, $split ); - } - - if( $count !== 'end' ) { - $split = mb_substr_split_unicode( $str, intval( $count ) ); - $str = substr( $str, 0, $split ); - } - - return $str; + return Fallback::mb_substr( $str, $start, $count ); } + /** @codeCoverageIgnore */ function mb_substr_split_unicode( $str, $splitPos ) { - if( $splitPos == 0 ) { - return 0; - } - - $byteLen = strlen( $str ); - - if( $splitPos > 0 ) { - if( $splitPos > 256 ) { - // Optimize large string offsets by skipping ahead N bytes. - // This will cut out most of our slow time on Latin-based text, - // and 1/2 to 1/3 on East European and Asian scripts. - $bytePos = $splitPos; - while ( $bytePos < $byteLen && $str{$bytePos} >= "\x80" && $str{$bytePos} < "\xc0" ) { - ++$bytePos; - } - $charPos = mb_strlen( substr( $str, 0, $bytePos ) ); - } else { - $charPos = 0; - $bytePos = 0; - } - - while( $charPos++ < $splitPos ) { - ++$bytePos; - // Move past any tail bytes - while ( $bytePos < $byteLen && $str{$bytePos} >= "\x80" && $str{$bytePos} < "\xc0" ) { - ++$bytePos; - } - } - } else { - $splitPosX = $splitPos + 1; - $charPos = 0; // relative to end of string; we don't care about the actual char position here - $bytePos = $byteLen; - while( $bytePos > 0 && $charPos-- >= $splitPosX ) { - --$bytePos; - // Move past any tail bytes - while ( $bytePos > 0 && $str{$bytePos} >= "\x80" && $str{$bytePos} < "\xc0" ) { - --$bytePos; - } - } - } - - return $bytePos; + return Fallback::mb_substr_split_unicode( $str, $splitPos ); } } if ( !function_exists( 'mb_strlen' ) ) { - /** - * Fallback implementation of mb_strlen, hardcoded to UTF-8. - * @param string $str - * @param string $enc optional encoding; ignored - * @return int - */ + /** @codeCoverageIgnore */ function mb_strlen( $str, $enc = '' ) { - $counts = count_chars( $str ); - $total = 0; - - // Count ASCII bytes - for( $i = 0; $i < 0x80; $i++ ) { - $total += $counts[$i]; - } - - // Count multibyte sequence heads - for( $i = 0xc0; $i < 0xff; $i++ ) { - $total += $counts[$i]; - } - return $total; + return Fallback::mb_strlen( $str, $enc ); } } - 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 - */ + /** @codeCoverageIgnore */ 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; - } + return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding ); } + } 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 - */ + /** @codeCoverageIgnore */ 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; - } + return Fallback::mb_strrpos( $haystack, $needle, $offset, $encoding ); } } + // Support for Wietse Venema's taint feature if ( !function_exists( 'istainted' ) ) { + /** @codeCoverageIgnore */ function istainted( $var ) { return 0; } + /** @codeCoverageIgnore */ function taint( $var, $level = 0 ) {} + /** @codeCoverageIgnore */ function untaint( $var, $level = 0 ) {} define( 'TC_HTML', 1 ); define( 'TC_SHELL', 1 ); @@ -199,7 +81,6 @@ if ( !function_exists( 'istainted' ) ) { } /// @endcond - /** * Like array_diff( $a, $b ) except that it works with two-dimensional arrays. */ @@ -214,7 +95,7 @@ function wfArrayDiff2_cmp( $a, $b ) { } else { reset( $a ); reset( $b ); - while( ( list( $keyA, $valueA ) = each( $a ) ) && ( list( $keyB, $valueB ) = each( $b ) ) ) { + while( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) { $cmp = strcmp( $valueA, $valueB ); if ( $cmp !== 0 ) { return $cmp; @@ -224,15 +105,6 @@ function wfArrayDiff2_cmp( $a, $b ) { } } -/** - * Seed Mersenne Twister - * No-op for compatibility; only necessary in PHP < 4.2.0 - * @deprecated. Remove in 1.18 - */ -function wfSeedRandom() { - wfDeprecated(__FUNCTION__); -} - /** * Get a random decimal value between 0 and 1, in a way * not likely to give duplicate values for any realistic @@ -306,7 +178,6 @@ function wfUrlencode( $s ) { function wfDebug( $text, $logonly = false ) { global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage; global $wgDebugLogPrefix, $wgShowDebug; - static $recursion = 0; static $cache = array(); // Cache of unoutputted messages $text = wfDebugTimer() . $text; @@ -319,21 +190,11 @@ function wfDebug( $text, $logonly = false ) { if ( ( $wgDebugComments || $wgShowDebug ) && !$logonly ) { $cache[] = $text; - if ( !isset( $wgOut ) ) { - return; - } - if ( !StubObject::isRealObject( $wgOut ) ) { - if ( $recursion ) { - return; - } - $recursion++; - $wgOut->_unstub(); - $recursion--; + if ( isset( $wgOut ) && StubObject::isRealObject( $wgOut ) ) { + // add the message and any cached messages to the output + array_map( array( $wgOut, 'debug' ), $cache ); + $cache = array(); } - - // add the message and possible cached ones to the output - array_map( array( $wgOut, 'debug' ), $cache ); - $cache = array(); } if ( $wgDebugLogFile != '' && !$wgProfileOnly ) { # Strip unprintables; they can switch terminal modes when binary data @@ -440,12 +301,21 @@ function wfErrorLog( $text, $file ) { } else { throw new MWException( __METHOD__ . ': Invalid UDP specification' ); } + // Clean it up for the multiplexer if ( strval( $prefix ) !== '' ) { $text = preg_replace( '/^/m', $prefix . ' ', $text ); + + // Limit to 64KB + if ( strlen( $text ) > 65534 ) { + $text = substr( $text, 0, 65534 ); + } + if ( substr( $text, -1 ) != "\n" ) { $text .= "\n"; } + } elseif ( strlen( $text ) > 65535 ) { + $text = substr( $text, 0, 65535 ); } $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP ); @@ -603,7 +473,7 @@ function wfUILang() { * The intention is that this function replaces all old wfMsg* functions. * @param $key \string Message key. * Varargs: normal message parameters. - * @return \type{Message} + * @return Message * @since 1.17 */ function wfMessage( $key /*...*/) { @@ -615,6 +485,19 @@ function wfMessage( $key /*...*/) { return new Message( $key, $params ); } +/** + * This function accepts multiple message keys and returns a message instance + * for the first message which is non-empty. If all messages are empty then an + * instance of the first message key is returned. + * Varargs: message keys + * @return \type{Message} + * @since 1.18 + */ +function wfMessageFallback( /*...*/ ) { + $args = func_get_args(); + return call_user_func_array( array( 'Message', 'newFallbackSequence' ), $args ); +} + /** * Get a message from anywhere, for the current user language. * @@ -739,11 +622,13 @@ 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: + * + * @deprecated in 1.18; use wfMessage() + * @param $key String */ function wfMsgWeirdKey( $key ) { $source = wfMsgGetKey( $key, false, true, false ); - if ( wfEmptyMsg( $key, $source ) ) { + if ( wfEmptyMsg( $key ) ) { return ''; } else { return $source; @@ -760,19 +645,14 @@ function wfMsgWeirdKey( $key ) { * @return string */ function wfMsgGetKey( $key, $useDB, $langCode = false, $transform = true ) { - global $wgMessageCache; - wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) ); - if ( !is_object( $wgMessageCache ) ) { - throw new MWException( 'Trying to get message before message cache is initialised' ); - } - - $message = $wgMessageCache->get( $key, $useDB, $langCode ); + $cache = MessageCache::singleton(); + $message = $cache->get( $key, $useDB, $langCode ); if( $message === false ) { $message = '<' . htmlspecialchars( $key ) . '>'; } elseif ( $transform ) { - $message = $wgMessageCache->transform( $message ); + $message = $cache->transform( $message ); } return $message; } @@ -854,7 +734,7 @@ function wfMsgWikiHtml( $key ) { * content: fetch message for content language instead of interface * Also can accept a single associative argument, of the form 'language' => 'xx': * language: Language object or language code to fetch message for - * (overriden by content), its behaviour with parser, parseinline + * (overriden by content), its behaviour with parse, parseinline * and parsemag is undefined. * Behavior for conflicting options (e.g., parse+parseinline) is undefined. */ @@ -881,12 +761,15 @@ function wfMsgExt( $key, $options ) { if( in_array( 'content', $options, true ) ) { $forContent = true; $langCode = true; + $langCodeObj = null; } elseif( array_key_exists( 'language', $options ) ) { $forContent = false; $langCode = wfGetLangObj( $options['language'] ); + $langCodeObj = $langCode; } else { $forContent = false; $langCode = false; + $langCodeObj = null; } $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false ); @@ -896,20 +779,16 @@ function wfMsgExt( $key, $options ) { } if( in_array( 'parse', $options, true ) ) { - $string = $wgOut->parse( $string, true, !$forContent ); + $string = $wgOut->parse( $string, true, !$forContent, $langCodeObj ); } elseif ( in_array( 'parseinline', $options, true ) ) { - $string = $wgOut->parse( $string, true, !$forContent ); + $string = $wgOut->parse( $string, true, !$forContent, $langCodeObj ); $m = array(); if( preg_match( '/^

(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { $string = $m[1]; } } elseif ( in_array( 'parsemag', $options, true ) ) { - global $wgMessageCache; - if ( isset( $wgMessageCache ) ) { - $string = $wgMessageCache->transform( $string, - !$forContent, - is_object( $langCode ) ? $langCode : null ); - } + $string = MessageCache::singleton()->transform( $string, + !$forContent, $langCodeObj ); } if ( in_array( 'escape', $options, true ) ) { @@ -1236,9 +1115,9 @@ function wfNumLink( $offset, $limit, $title, $query = '' ) { * * @return bool Whereas client accept gzip compression */ -function wfClientAcceptsGzip() { +function wfClientAcceptsGzip( $force = false ) { static $result = null; - if ( $result === null ) { + if ( $result === null || $force ) { $result = false; if( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) { # FIXME: we may want to blacklist some broken browsers @@ -1279,47 +1158,22 @@ function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { * Escapes the given text so that it may be output using addWikiText() * without any linking, formatting, etc. making its way through. This * is achieved by substituting certain characters with HTML entities. - * As required by the callers, is not used. It currently does - * not filter out characters which have special meaning only at the - * start of a line, such as "*". + * As required by the callers, is not used. * * @param $text String: text to be escaped */ function wfEscapeWikiText( $text ) { - $text = str_replace( - array( '[', '|', ']', '\'', 'ISBN ', - 'RFC ', '://', "\n=", '{{', '}}' ), - array( '[', '|', ']', ''', 'ISBN ', - 'RFC ', '://', "\n=", '{{', '}}' ), - htmlspecialchars( $text ) - ); - return $text; -} - -/** - * @todo document - */ -function wfQuotedPrintable( $string, $charset = '' ) { - # Probably incomplete; see RFC 2045 - if( empty( $charset ) ) { - global $wgInputEncoding; - $charset = $wgInputEncoding; - } - $charset = strtoupper( $charset ); - $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ? - - $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff='; - $replace = $illegal . '\t ?_'; - if( !preg_match( "/[$illegal]/", $string ) ) { - return $string; - } - $out = "=?$charset?Q?"; - $out .= preg_replace( "/([$replace])/e", 'sprintf("=%02X",ord("$1"))', $string ); - $out .= '?='; - return $out; + $text = strtr( "\n$text", array( + '"' => '"', '&' => '&', "'" => ''', '<' => '<', + '=' => '=', '>' => '>', '[' => '[', ']' => ']', + '{' => '{', '|' => '|', '}' => '}', + "\n#" => "\n#", "\n*" => "\n*", + "\n:" => "\n:", "\n;" => "\n;", + '://' => '://', 'ISBN ' => 'ISBN ', 'RFC ' => 'RFC ', + ) ); + return substr( $text, 1 ); } - /** * @todo document * @return float @@ -1331,10 +1185,11 @@ function wfTime() { /** * Sets dest to source and returns the original value of dest * If source is NULL, it just returns the value, it doesn't set the variable + * If force is true, it will set the value even if source is NULL */ -function wfSetVar( &$dest, $source ) { +function wfSetVar( &$dest, $source, $force = false ) { $temp = $dest; - if ( !is_null( $source ) ) { + if ( !is_null( $source ) || $force ) { $dest = $source; } return $temp; @@ -1486,8 +1341,12 @@ function wfEscapeShellArg( ) { } if ( wfIsWindows() ) { - // Escaping for an MSVC-style command line parser - // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html + // Escaping for an MSVC-style command line parser and CMD.EXE + // Refs: + // * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html + // * http://technet.microsoft.com/en-us/library/cc723564.aspx + // * Bug #13518 + // * CR r63214 // Double the backslashes before any double quotes. Escape the double quotes. $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); $arg = ''; @@ -1740,7 +1599,13 @@ function wfResetOutputBuffers( $resetGzipEncoding = true ) { if( $status['name'] == 'ob_gzhandler' ) { // Reset the 'Content-Encoding' field set by this handler // so we can start fresh. - header( 'Content-Encoding:' ); + if ( function_exists( 'header_remove' ) ) { + // Available since PHP 5.3.0 + header_remove( 'Content-Encoding' ); + } else { + // We need to provide a valid content-coding. See bug 28069 + header( 'Content-Encoding: identity' ); + } break; } } @@ -1981,13 +1846,16 @@ define( 'TS_ISO_8601_BASIC', 9 ); * 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 + * @return Mixed: String / false The same date in the format specified in $outputtype or false */ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { $uts = 0; $da = array(); - if ( $ts === 0 ) { + $strtime = ''; + + if ( !$ts ) { // We want to catch 0, '', null... but not date strings starting with a letter. $uts = time(); + $strtime = "@$uts"; } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) { # TS_DB } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) { @@ -1997,10 +1865,11 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { } elseif ( preg_match( '/^-?\d{1,13}$/D', $ts ) ) { # TS_UNIX $uts = $ts; + $strtime = "@$ts"; // Undocumented? } 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 ) ) ); + $strtime = 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 ) ) { # TS_ISO_8601 } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) { @@ -2011,22 +1880,27 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { # TS_POSTGRES } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/',$ts,$da)) { # TS_DB2 - } elseif ( preg_match( '/^[A-Z][a-z]{2}, \d\d [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d/', $ts ) ) { - # TS_RFC2822 - $uts = strtotime( $ts ); + } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week + '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' . # dd Mon yyyy + '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss + # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171 + # The regex is a superset of rfc2822 for readability + $strtime = strtok( $ts, ';' ); + } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) { + # TS_RFC850 + $strtime = $ts; + } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) { + # asctime + $strtime = $ts; } else { - # Bogus value; fall back to the epoch... - wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); - $uts = 0; - } + # Bogus value... + wfDebug("wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n"); - if (count( $da ) ) { - // Warning! gmmktime() acts oddly if the month or day is set to 0 - // We may want to handle that explicitly at some point - $uts = gmmktime( (int)$da[4], (int)$da[5], (int)$da[6], - (int)$da[2], (int)$da[3], (int)$da[1] ); + return false; } + + static $formats = array( TS_UNIX => 'U', TS_MW => 'YmdHis', @@ -2044,11 +1918,45 @@ function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { throw new MWException( 'wfTimestamp() called with illegal output type.' ); } - if ( TS_UNIX == $outputtype ) { - return $uts; - } + if ( function_exists( "date_create" ) ) { + if ( count( $da ) ) { + $ds = sprintf("%04d-%02d-%02dT%02d:%02d:%02d.00+00:00", + (int)$da[1], (int)$da[2], (int)$da[3], + (int)$da[4], (int)$da[5], (int)$da[6]); - $output = gmdate( $formats[$outputtype], $uts ); + $d = date_create( $ds, new DateTimeZone( 'GMT' ) ); + } elseif ( $strtime ) { + $d = date_create( $strtime, new DateTimeZone( 'GMT' ) ); + } else { + return false; + } + + if ( !$d ) { + wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n"); + return false; + } + + $output = $d->format( $formats[$outputtype] ); + } else { + if ( count( $da ) ) { + // Warning! gmmktime() acts oddly if the month or day is set to 0 + // We may want to handle that explicitly at some point + $uts = gmmktime( (int)$da[4], (int)$da[5], (int)$da[6], + (int)$da[2], (int)$da[3], (int)$da[1] ); + } elseif ( $strtime ) { + $uts = strtotime( $strtime ); + } + + if ( $uts === false ) { + wfDebug("wfTimestamp() can't parse the timestamp (non 32-bit time? Update php): $outputtype; $ts\n"); + return false; + } + + if ( TS_UNIX == $outputtype ) { + return $uts; + } + $output = gmdate( $formats[$outputtype], $uts ); + } if ( ( $outputtype == TS_RFC2822 ) || ( $outputtype == TS_POSTGRES ) ) { $output .= ' GMT'; @@ -2094,110 +2002,9 @@ function swap( &$x, &$y ) { $y = $z; } -function wfGetCachedNotice( $name ) { - global $wgOut, $wgRenderHashAppend, $parserMemc; - $fname = 'wfGetCachedNotice'; - wfProfileIn( $fname ); - - $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 ); - } - } - - // Use the extra hash appender to let eg SSL variants separately cache. - $key = wfMemcKey( $name . $wgRenderHashAppend ); - $cachedNotice = $parserMemc->get( $key ); - if( is_array( $cachedNotice ) ) { - if( md5( $notice ) == $cachedNotice['hash'] ) { - $notice = $cachedNotice['html']; - } else { - $needParse = true; - } - } else { - $needParse = true; - } - - if( $needParse ) { - if( is_object( $wgOut ) ) { - $parsed = $wgOut->parse( $notice ); - $parserMemc->set( $key, array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 ); - $notice = $parsed; - } else { - wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' . "\n" ); - $notice = ''; - } - } - $notice = '

' .$notice . '
'; - wfProfileOut( $fname ); - return $notice; -} - -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 { - $namespaceNotice = ''; - } - - wfProfileOut( $fname ); - return $namespaceNotice; -} - -function wfGetSiteNotice() { - global $wgUser; - $fname = 'wfGetSiteNotice'; - wfProfileIn( $fname ); - $siteNotice = ''; - - if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) { - if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) { - $siteNotice = wfGetCachedNotice( 'sitenotice' ); - } else { - $anonNotice = wfGetCachedNotice( 'anonnotice' ); - if( !$anonNotice ) { - $siteNotice = wfGetCachedNotice( 'sitenotice' ); - } else { - $siteNotice = $anonNotice; - } - } - if( !$siteNotice ) { - $siteNotice = wfGetCachedNotice( 'default' ); - } - } - - wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) ); - wfProfileOut( $fname ); - return $siteNotice; -} - /** * BC wrapper for MimeMagic::singleton() - * @deprecated No longer needed as of 1.17 (r68836). + * @deprecated No longer needed as of 1.17 (r68836). Remove in 1.19. */ function &wfGetMimeMagic() { wfDeprecated( __FUNCTION__ ); @@ -2270,15 +2077,20 @@ function wfMkdirParents( $dir, $mode = null, $caller = null ) { /** * Increment a statistics counter */ -function wfIncrStats( $key ) { +function wfIncrStats( $key, $count = 1 ) { global $wgStatsMethod; + $count = intval( $count ); + if( $wgStatsMethod == 'udp' ) { - global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname; + global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname, $wgAggregateStatsID; static $socket; + + $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : $wgDBname; + if ( !$socket ) { $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP ); - $statline = "stats/{$wgDBname} - 1 1 1 1 1 -total\n"; + $statline = "stats/{$id} - {$count} 1 1 1 1 -total\n"; socket_sendto( $socket, $statline, @@ -2288,7 +2100,7 @@ function wfIncrStats( $key ) { $wgUDPProfilerPort ); } - $statline = "stats/{$wgDBname} - 1 1 1 1 1 {$key}\n"; + $statline = "stats/{$id} - {$count} 1 1 1 1 {$key}\n"; wfSuppressWarnings(); socket_sendto( $socket, @@ -2302,8 +2114,8 @@ function wfIncrStats( $key ) { } elseif( $wgStatsMethod == 'cache' ) { global $wgMemc; $key = wfMemcKey( 'stats', $key ); - if ( is_null( $wgMemc->incr( $key ) ) ) { - $wgMemc->add( $key, 1 ); + if ( is_null( $wgMemc->incr( $key, $count ) ) ) { + $wgMemc->add( $key, $count ); } } else { // Disabled @@ -2356,8 +2168,7 @@ function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { * @return Boolean True if the message *doesn't* exist. */ function wfEmptyMsg( $key ) { - global $wgMessageCache; - return $wgMessageCache->get( $key, /*useDB*/true, /*content*/false ) === false; + return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false; } /** @@ -2365,10 +2176,14 @@ function wfEmptyMsg( $key ) { * * @param $needle String * @param $str String + * @param $insensitive Boolean * @return Boolean */ -function in_string( $needle, $str ) { - return strpos( $str, $needle ) !== false; +function in_string( $needle, $str, $insensitive = false ) { + $func = 'strpos'; + if( $insensitive ) $func = 'stripos'; + + return $func( $str, $needle ) !== false; } function wfSpecialList( $page, $details ) { @@ -2467,7 +2282,7 @@ function wfDl( $extension ) { * @param $cmd String Command line, properly escaped for shell. * @param &$retval optional, will receive the program's exit code. * (non-zero is usually failure) - * @param $environ Array optional environment variables which should be + * @param $environ Array optional environment variables which should be * added to the executed command environment. * @return collected stdout as a string (trailing newlines stripped) */ @@ -2502,22 +2317,22 @@ function wfShellExec( $cmd, &$retval = null, $environ = array() ) { $envcmd = ''; foreach( $environ as $k => $v ) { if ( wfIsWindows() ) { - /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves - * appear in the environment variable, so we must use carat escaping as documented in - * http://technet.microsoft.com/en-us/library/cc723564.aspx - * Note however that the quote isn't listed there, but is needed, and the parentheses + /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves + * appear in the environment variable, so we must use carat escaping as documented in + * http://technet.microsoft.com/en-us/library/cc723564.aspx + * Note however that the quote isn't listed there, but is needed, and the parentheses * are listed there but doesn't appear to need it. */ - $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . ' && '; + $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& '; } else { - /* Assume this is a POSIX shell, thus required to accept variable assignments before the command + /* Assume this is a POSIX shell, thus required to accept variable assignments before the command * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01 */ $envcmd .= "$k=" . escapeshellarg( $v ) . ' '; } } $cmd = $envcmd . $cmd; - + if ( wfIsWindows() ) { if ( version_compare( PHP_VERSION, '5.3.0', '<' ) && /* Fixed in 5.3.0 :) */ ( version_compare( PHP_VERSION, '5.2.1', '>=' ) || php_uname( 's' ) == 'Windows NT' ) ) @@ -2816,18 +2631,36 @@ function wfMakeUrlIndex( $url ) { /** * Do any deferred updates and clear the list - * TODO: This could be in Wiki.php if that class made any sense at all + * + * @param $commit String: set to 'commit' to commit after every update to + * prevent lock contention */ -function wfDoUpdates() { - global $wgPostCommitUpdateList, $wgDeferredUpdateList; - foreach ( $wgDeferredUpdateList as $update ) { - $update->doUpdate(); +function wfDoUpdates( $commit = '' ) { + global $wgDeferredUpdateList; + + wfProfileIn( __METHOD__ ); + + // No need to get master connections in case of empty updates array + if ( !count( $wgDeferredUpdateList ) ) { + wfProfileOut( __METHOD__ ); + return; } - foreach ( $wgPostCommitUpdateList as $update ) { + + $doCommit = $commit == 'commit'; + if ( $doCommit ) { + $dbw = wfGetDB( DB_MASTER ); + } + + foreach ( $wgDeferredUpdateList as $update ) { $update->doUpdate(); + + if ( $doCommit && $dbw->trxLevel() ) { + $dbw->commit(); + } } + $wgDeferredUpdateList = array(); - $wgPostCommitUpdateList = array(); + wfProfileOut( __METHOD__ ); } /** @@ -2921,34 +2754,14 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = t * Create an object with a given name and an array of construct parameters * @param $name String * @param $p Array: parameters + * @deprecated */ 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' ); - } + return MWFunction::newObj( $name, $p ); } function wfHttpOnlySafe() { global $wgHttpOnlyBlacklist; - if( !version_compare( '5.2', PHP_VERSION, '<' ) ) { - return false; - } if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) { foreach( $wgHttpOnlyBlacklist as $regex ) { @@ -3132,6 +2945,7 @@ function wfGetLB( $wiki = false ) { /** * Get the load balancer factory object + * @return LBFactory */ function &wfGetLBFactory() { return LBFactory::singleton(); @@ -3163,7 +2977,7 @@ function wfFindFile( $title, $options = array() ) { /** * Get an object referring to a locally registered file. * Returns a valid placeholder object if the file does not exist. - * @param $title Either a string or Title object + * @param $title Title or String * @return File, or null if passed an invalid Title */ function wfLocalFile( $title ) { @@ -3174,6 +2988,7 @@ function wfLocalFile( $title ) { * Should low-performance queries be disabled? * * @return Boolean + * @codeCoverageIgnore */ function wfQueriesMustScale() { global $wgMiserMode; @@ -3231,9 +3046,11 @@ function wfBoolToStr( $value ) { /** * Load an extension messages file - * @deprecated in 1.16 (warnings in 1.18, removed in ?) + * @deprecated in 1.16, warnings in 1.18, remove in 1.20 + * @codeCoverageIgnore */ function wfLoadExtensionMessages( $extensionName, $langcode = false ) { + wfDeprecated( __FUNCTION__ ); } /** @@ -3352,8 +3169,8 @@ function wfWaitForSlaves( $maxLag, $wiki = false ) { } /** - * 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. + * Used to be used for outputting text in the installer/updater + * @deprecated Warnings in 1.19, removal in 1.20 */ function wfOut( $s ) { global $wgCommandLineMode; @@ -3368,6 +3185,7 @@ function wfOut( $s ) { /** * Count down from $n to zero on the terminal, with a one-second pause * between showing each number. For use in command-line scripts. + * @codeCoverageIgnore */ function wfCountDown( $n ) { for ( $i = $n; $i >= 0; $i-- ) { @@ -3387,6 +3205,7 @@ function wfCountDown( $n ) { * Generate a random 32-character hexadecimal token. * @param $salt Mixed: some sort of salt, if necessary, to add to random * characters before hashing. + * @codeCoverageIgnore */ function wfGenerateToken( $salt = '' ) { $salt = serialize( $salt ); @@ -3433,10 +3252,13 @@ function wfArrayInsertAfter( $array, $insert, $after ) { } /* Recursively converts the parameter (an object) to an array with the same data */ -function wfObjectToArray( $object, $recursive = true ) { +function wfObjectToArray( $objOrArray, $recursive = true ) { $array = array(); - foreach ( get_object_vars( $object ) as $key => $value ) { - if ( is_object( $value ) && $recursive ) { + if( is_object( $objOrArray ) ) { + $objOrArray = get_object_vars( $objOrArray ); + } + foreach ( $objOrArray as $key => $value ) { + if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) { $value = wfObjectToArray( $value ); } @@ -3503,6 +3325,7 @@ function wfShorthandToInteger( $string = '' ) { /** * Get the normalised IETF language tag + * See unit test for examples. * @param $code String: The language code. * @return $langCode String: The language code which complying with BCP 47 standards. */ @@ -3510,12 +3333,15 @@ function wfBCP47( $code ) { $codeSegment = explode( '-', $code ); foreach ( $codeSegment as $segNo => $seg ) { if ( count( $codeSegment ) > 0 ) { + // when previous segment is x, it is a private segment and should be lc + if( $segNo > 0 && strtolower( $codeSegment[($segNo - 1)] ) == 'x') { + $codeBCP[$segNo] = strtolower( $seg ); // ISO 3166 country code - if ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) { + } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) { $codeBCP[$segNo] = strtoupper( $seg ); // ISO 15924 script code } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) { - $codeBCP[$segNo] = ucfirst( $seg ); + $codeBCP[$segNo] = ucfirst( strtolower( $seg ) ); // Use lowercase for other cases } else { $codeBCP[$segNo] = strtolower( $seg ); @@ -3540,22 +3366,32 @@ function wfArrayMap( $function, $input ) { return $ret; } + /** - * Returns the PackageRepository object for interaction with the package repository. - * - * TODO: Make the repository type also configurable. - * - * @since 1.17 - * - * @return PackageRepository + * Get a cache object. + * @param $inputType Cache type, one the the CACHE_* constants. + * + * @return BagOStuff */ -function wfGetRepository() { - global $wgRepositoryApiLocation; - static $repository = false; - - if ( $repository === false ) { - $repository = new DistributionRepository( $wgRepositoryApiLocation ); - } - - return $repository; +function wfGetCache( $inputType ) { + return ObjectCache::getInstance( $inputType ); +} + +/** Get the main cache object */ +function wfGetMainCache() { + global $wgMainCacheType; + return ObjectCache::getInstance( $wgMainCacheType ); } + +/** Get the cache object used by the message cache */ +function wfGetMessageCacheStorage() { + global $wgMessageCacheType; + return ObjectCache::getInstance( $wgMessageCacheType ); +} + +/** Get the cache object used by the parser cache */ +function wfGetParserCacheStorage() { + global $wgParserCacheType; + return ObjectCache::getInstance( $wgParserCacheType ); +} +