/includes/GlobalFunctions.php
PHP | 3884 lines | 2117 code | 343 blank | 1424 comment | 486 complexity | 93ac49f1c5c91e191077aca2b169746c MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * Global functions used everywhere
- * @file
- */
- if ( !defined( 'MEDIAWIKI' ) ) {
- die( "This file is part of MediaWiki, it is not a valid entry point" );
- }
- // Hide compatibility functions from Doxygen
- /// @cond
- /**
- * Compatibility functions
- *
- * We support PHP 5.2.3 and up.
- * Re-implementations of newer functions or functions in non-standard
- * PHP extensions may be included here.
- */
- if( !function_exists( 'iconv' ) ) {
- /** @codeCoverageIgnore */
- function iconv( $from, $to, $string ) {
- return Fallback::iconv( $from, $to, $string );
- }
- }
- if ( !function_exists( 'mb_substr' ) ) {
- /** @codeCoverageIgnore */
- function mb_substr( $str, $start, $count='end' ) {
- return Fallback::mb_substr( $str, $start, $count );
- }
- /** @codeCoverageIgnore */
- function mb_substr_split_unicode( $str, $splitPos ) {
- return Fallback::mb_substr_split_unicode( $str, $splitPos );
- }
- }
- if ( !function_exists( 'mb_strlen' ) ) {
- /** @codeCoverageIgnore */
- function mb_strlen( $str, $enc = '' ) {
- return Fallback::mb_strlen( $str, $enc );
- }
- }
- if( !function_exists( 'mb_strpos' ) ) {
- /** @codeCoverageIgnore */
- function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
- return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding );
- }
- }
- if( !function_exists( 'mb_strrpos' ) ) {
- /** @codeCoverageIgnore */
- function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
- 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 );
- define( 'TC_MYSQL', 1 );
- define( 'TC_PCRE', 1 );
- define( 'TC_SELF', 1 );
- }
- /// @endcond
- /**
- * Like array_diff( $a, $b ) except that it works with two-dimensional arrays.
- * @param $a array
- * @param $b array
- * @return array
- */
- function wfArrayDiff2( $a, $b ) {
- return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
- }
- /**
- * @param $a
- * @param $b
- * @return int
- */
- function wfArrayDiff2_cmp( $a, $b ) {
- if ( !is_array( $a ) ) {
- return strcmp( $a, $b );
- } elseif ( count( $a ) !== count( $b ) ) {
- return count( $a ) < count( $b ) ? -1 : 1;
- } else {
- reset( $a );
- reset( $b );
- while( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
- $cmp = strcmp( $valueA, $valueB );
- if ( $cmp !== 0 ) {
- return $cmp;
- }
- }
- return 0;
- }
- }
- /**
- * Array lookup
- * Returns an array where the values in the first array are replaced by the
- * values in the second array with the corresponding keys
- *
- * @param $a Array
- * @param $b Array
- * @return array
- */
- function wfArrayLookup( $a, $b ) {
- return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) );
- }
- /**
- * Appends to second array if $value differs from that in $default
- *
- * @param $key String|Int
- * @param $value Mixed
- * @param $default Mixed
- * @param $changed Array to alter
- */
- function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
- if ( is_null( $changed ) ) {
- throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
- }
- if ( $default[$key] !== $value ) {
- $changed[$key] = $value;
- }
- }
- /**
- * Backwards array plus for people who haven't bothered to read the PHP manual
- * XXX: will not darn your socks for you.
- *
- * @param $array1 Array
- * @param [$array2, [...]] Arrays
- * @return Array
- */
- function wfArrayMerge( $array1/* ... */ ) {
- $args = func_get_args();
- $args = array_reverse( $args, true );
- $out = array();
- foreach ( $args as $arg ) {
- $out += $arg;
- }
- return $out;
- }
- /**
- * Merge arrays in the style of getUserPermissionsErrors, with duplicate removal
- * e.g.
- * wfMergeErrorArrays(
- * array( array( 'x' ) ),
- * array( array( 'x', '2' ) ),
- * array( array( 'x' ) ),
- * array( array( 'y' ) )
- * );
- * returns:
- * array(
- * array( 'x', '2' ),
- * array( 'x' ),
- * array( 'y' )
- * )
- * @param varargs
- * @return Array
- */
- function wfMergeErrorArrays( /*...*/ ) {
- $args = func_get_args();
- $out = array();
- foreach ( $args as $errors ) {
- foreach ( $errors as $params ) {
- # @todo FIXME: Sometimes get nested arrays for $params,
- # which leads to E_NOTICEs
- $spec = implode( "\t", $params );
- $out[$spec] = $params;
- }
- }
- return array_values( $out );
- }
- /**
- * Insert array into another array after the specified *KEY*
- *
- * @param $array Array: The array.
- * @param $insert Array: The array to insert.
- * @param $after Mixed: The key to insert after
- * @return Array
- */
- function wfArrayInsertAfter( $array, $insert, $after ) {
- // Find the offset of the element to insert after.
- $keys = array_keys( $array );
- $offsetByKey = array_flip( $keys );
- $offset = $offsetByKey[$after];
- // Insert at the specified offset
- $before = array_slice( $array, 0, $offset + 1, true );
- $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
- $output = $before + $insert + $after;
- return $output;
- }
- /**
- * Recursively converts the parameter (an object) to an array with the same data
- *
- * @param $objOrArray Object|Array
- * @param $recursive Bool
- * @return Array
- */
- function wfObjectToArray( $objOrArray, $recursive = true ) {
- $array = array();
- 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 );
- }
- $array[$key] = $value;
- }
- return $array;
- }
- /**
- * Wrapper around array_map() which also taints variables
- *
- * @param $function Callback
- * @param $input Array
- * @return Array
- */
- function wfArrayMap( $function, $input ) {
- $ret = array_map( $function, $input );
- foreach ( $ret as $key => $value ) {
- $taint = istainted( $input[$key] );
- if ( $taint ) {
- taint( $ret[$key], $taint );
- }
- }
- return $ret;
- }
- /**
- * 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() + 1;
- $rand = number_format( ( mt_rand() * $max + mt_rand() )
- / $max / $max, 12, '.', '' );
- return $rand;
- }
- /**
- * We want some things to be included as literal characters in our title URLs
- * for prettiness, which urlencode encodes by default. According to RFC 1738,
- * all of the following should be safe:
- *
- * ;:@&=$-_.+!*'(),
- *
- * But + is not safe because it's used to indicate a space; &= are only safe in
- * paths and not in queries (and we don't distinguish here); ' seems kind of
- * scary; and urlencode() doesn't touch -_. to begin with. Plus, although /
- * is reserved, we don't care. So the list we unescape is:
- *
- * ;:@$!*(),/
- *
- * However, IIS7 redirects fail when the url contains a colon (Bug 22709),
- * so no fancy : for IIS7.
- *
- * %2F in the page titles seems to fatally break for some reason.
- *
- * @param $s String:
- * @return string
- */
- function wfUrlencode( $s ) {
- static $needle;
- if ( is_null( $s ) ) {
- $needle = null;
- return '';
- }
- if ( is_null( $needle ) ) {
- $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
- if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) ) {
- $needle[] = '%3A';
- }
- }
- $s = urlencode( $s );
- $s = str_ireplace(
- $needle,
- array( ';', '@', '$', '!', '*', '(', ')', ',', '/', ':' ),
- $s
- );
- return $s;
- }
- /**
- * This function takes two arrays as input, and returns a CGI-style string, e.g.
- * "days=7&limit=100". Options in the first array override options in the second.
- * Options set to null or false will not be output.
- *
- * @param $array1 Array ( String|Array )
- * @param $array2 Array ( String|Array )
- * @param $prefix String
- * @return String
- */
- function wfArrayToCGI( $array1, $array2 = null, $prefix = '' ) {
- if ( !is_null( $array2 ) ) {
- $array1 = $array1 + $array2;
- }
- $cgi = '';
- foreach ( $array1 as $key => $value ) {
- if ( !is_null($value) && $value !== false ) {
- if ( $cgi != '' ) {
- $cgi .= '&';
- }
- if ( $prefix !== '' ) {
- $key = $prefix . "[$key]";
- }
- if ( is_array( $value ) ) {
- $firstTime = true;
- foreach ( $value as $k => $v ) {
- $cgi .= $firstTime ? '' : '&';
- if ( is_array( $v ) ) {
- $cgi .= wfArrayToCGI( $v, null, $key . "[$k]" );
- } else {
- $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
- }
- $firstTime = false;
- }
- } else {
- if ( is_object( $value ) ) {
- $value = $value->__toString();
- }
- $cgi .= urlencode( $key ) . '=' . urlencode( $value );
- }
- }
- }
- return $cgi;
- }
- /**
- * This is the logical opposite of wfArrayToCGI(): it accepts a query string as
- * its argument and returns the same string in array form. This allows compa-
- * tibility with legacy functions that accept raw query strings instead of nice
- * arrays. Of course, keys and values are urldecode()d.
- *
- * @param $query String: query string
- * @return array Array version of input
- */
- function wfCgiToArray( $query ) {
- if ( isset( $query[0] ) && $query[0] == '?' ) {
- $query = substr( $query, 1 );
- }
- $bits = explode( '&', $query );
- $ret = array();
- foreach ( $bits as $bit ) {
- if ( $bit === '' ) {
- continue;
- }
- if ( strpos( $bit, '=' ) === false ) {
- // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
- $key = $bit;
- $value = '';
- } else {
- list( $key, $value ) = explode( '=', $bit );
- }
- $key = urldecode( $key );
- $value = urldecode( $value );
- if ( strpos( $key, '[' ) !== false ) {
- $keys = array_reverse( explode( '[', $key ) );
- $key = array_pop( $keys );
- $temp = $value;
- foreach ( $keys as $k ) {
- $k = substr( $k, 0, -1 );
- $temp = array( $k => $temp );
- }
- if ( isset( $ret[$key] ) ) {
- $ret[$key] = array_merge( $ret[$key], $temp );
- } else {
- $ret[$key] = $temp;
- }
- } else {
- $ret[$key] = $value;
- }
- }
- return $ret;
- }
- /**
- * Append a query string to an existing URL, which may or may not already
- * have query string parameters already. If so, they will be combined.
- *
- * @param $url String
- * @param $query Mixed: string or associative array
- * @return string
- */
- function wfAppendQuery( $url, $query ) {
- if ( is_array( $query ) ) {
- $query = wfArrayToCGI( $query );
- }
- if( $query != '' ) {
- if( false === strpos( $url, '?' ) ) {
- $url .= '?';
- } else {
- $url .= '&';
- }
- $url .= $query;
- }
- return $url;
- }
- /**
- * Expand a potentially local URL to a fully-qualified URL. Assumes $wgServer
- * is correct.
- *
- * The meaning of the PROTO_* constants is as follows:
- * PROTO_HTTP: Output a URL starting with http://
- * PROTO_HTTPS: Output a URL starting with https://
- * PROTO_RELATIVE: Output a URL starting with // (protocol-relative URL)
- * PROTO_CURRENT: Output a URL starting with either http:// or https:// , depending on which protocol was used for the current incoming request
- * PROTO_CANONICAL: For URLs without a domain, like /w/index.php , use $wgCanonicalServer. For protocol-relative URLs, use the protocol of $wgCanonicalServer
- * PROTO_INTERNAL: Like PROTO_CANONICAL, but uses $wgInternalServer instead of $wgCanonicalServer
- *
- * @todo this won't work with current-path-relative URLs
- * like "subdir/foo.html", etc.
- *
- * @param $url String: either fully-qualified or a local path + query
- * @param $defaultProto Mixed: one of the PROTO_* constants. Determines the
- * protocol to use if $url or $wgServer is
- * protocol-relative
- * @return string Fully-qualified URL, current-path-relative URL or false if
- * no valid URL can be constructed
- */
- function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
- global $wgServer, $wgCanonicalServer, $wgInternalServer;
- $serverUrl = $wgServer;
- if ( $defaultProto === PROTO_CANONICAL ) {
- $serverUrl = $wgCanonicalServer;
- }
- // Make $wgInternalServer fall back to $wgServer if not set
- if ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
- $serverUrl = $wgInternalServer;
- }
- if ( $defaultProto === PROTO_CURRENT ) {
- $defaultProto = WebRequest::detectProtocol() . '://';
- }
- // Analyze $serverUrl to obtain its protocol
- $bits = wfParseUrl( $serverUrl );
- $serverHasProto = $bits && $bits['scheme'] != '';
- if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
- if ( $serverHasProto ) {
- $defaultProto = $bits['scheme'] . '://';
- } else {
- // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. This really isn't supposed to happen
- // Fall back to HTTP in this ridiculous case
- $defaultProto = PROTO_HTTP;
- }
- }
- $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
- if ( substr( $url, 0, 2 ) == '//' ) {
- $url = $defaultProtoWithoutSlashes . $url;
- } elseif ( substr( $url, 0, 1 ) == '/' ) {
- // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone
- $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
- }
- $bits = wfParseUrl( $url );
- if ( $bits && isset( $bits['path'] ) ) {
- $bits['path'] = wfRemoveDotSegments( $bits['path'] );
- return wfAssembleUrl( $bits );
- } elseif ( $bits ) {
- # No path to expand
- return $url;
- } elseif ( substr( $url, 0, 1 ) != '/' ) {
- # URL is a relative path
- return wfRemoveDotSegments( $url );
- }
- # Expanded URL is not valid.
- return false;
- }
- /**
- * This function will reassemble a URL parsed with wfParseURL. This is useful
- * if you need to edit part of a URL and put it back together.
- *
- * This is the basic structure used (brackets contain keys for $urlParts):
- * [scheme][delimiter][user]:[pass]@[host]:[port][path]?[query]#[fragment]
- *
- * @todo Need to integrate this into wfExpandUrl (bug 32168)
- *
- * @since 1.19
- * @param $urlParts Array URL parts, as output from wfParseUrl
- * @return string URL assembled from its component parts
- */
- function wfAssembleUrl( $urlParts ) {
- $result = '';
- if ( isset( $urlParts['delimiter'] ) ) {
- if ( isset( $urlParts['scheme'] ) ) {
- $result .= $urlParts['scheme'];
- }
- $result .= $urlParts['delimiter'];
- }
- if ( isset( $urlParts['host'] ) ) {
- if ( isset( $urlParts['user'] ) ) {
- $result .= $urlParts['user'];
- if ( isset( $urlParts['pass'] ) ) {
- $result .= ':' . $urlParts['pass'];
- }
- $result .= '@';
- }
- $result .= $urlParts['host'];
- if ( isset( $urlParts['port'] ) ) {
- $result .= ':' . $urlParts['port'];
- }
- }
- if ( isset( $urlParts['path'] ) ) {
- $result .= $urlParts['path'];
- }
- if ( isset( $urlParts['query'] ) ) {
- $result .= '?' . $urlParts['query'];
- }
- if ( isset( $urlParts['fragment'] ) ) {
- $result .= '#' . $urlParts['fragment'];
- }
- return $result;
- }
- /**
- * Remove all dot-segments in the provided URL path. For example,
- * '/a/./b/../c/' becomes '/a/c/'. For details on the algorithm, please see
- * RFC3986 section 5.2.4.
- *
- * @todo Need to integrate this into wfExpandUrl (bug 32168)
- *
- * @param $urlPath String URL path, potentially containing dot-segments
- * @return string URL path with all dot-segments removed
- */
- function wfRemoveDotSegments( $urlPath ) {
- $output = '';
- $inputOffset = 0;
- $inputLength = strlen( $urlPath );
- while ( $inputOffset < $inputLength ) {
- $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
- $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
- $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
- $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
- $trimOutput = false;
- if ( $prefixLengthTwo == './' ) {
- # Step A, remove leading "./"
- $inputOffset += 2;
- } elseif ( $prefixLengthThree == '../' ) {
- # Step A, remove leading "../"
- $inputOffset += 3;
- } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
- # Step B, replace leading "/.$" with "/"
- $inputOffset += 1;
- $urlPath[$inputOffset] = '/';
- } elseif ( $prefixLengthThree == '/./' ) {
- # Step B, replace leading "/./" with "/"
- $inputOffset += 2;
- } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
- # Step C, replace leading "/..$" with "/" and
- # remove last path component in output
- $inputOffset += 2;
- $urlPath[$inputOffset] = '/';
- $trimOutput = true;
- } elseif ( $prefixLengthFour == '/../' ) {
- # Step C, replace leading "/../" with "/" and
- # remove last path component in output
- $inputOffset += 3;
- $trimOutput = true;
- } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
- # Step D, remove "^.$"
- $inputOffset += 1;
- } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
- # Step D, remove "^..$"
- $inputOffset += 2;
- } else {
- # Step E, move leading path segment to output
- if ( $prefixLengthOne == '/' ) {
- $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
- } else {
- $slashPos = strpos( $urlPath, '/', $inputOffset );
- }
- if ( $slashPos === false ) {
- $output .= substr( $urlPath, $inputOffset );
- $inputOffset = $inputLength;
- } else {
- $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
- $inputOffset += $slashPos - $inputOffset;
- }
- }
- if ( $trimOutput ) {
- $slashPos = strrpos( $output, '/' );
- if ( $slashPos === false ) {
- $output = '';
- } else {
- $output = substr( $output, 0, $slashPos );
- }
- }
- }
- return $output;
- }
- /**
- * Returns a regular expression of url protocols
- *
- * @param $includeProtocolRelative bool If false, remove '//' from the returned protocol list.
- * DO NOT USE this directly, use wfUrlProtocolsWithoutProtRel() instead
- * @return String
- */
- function wfUrlProtocols( $includeProtocolRelative = true ) {
- global $wgUrlProtocols;
- // Cache return values separately based on $includeProtocolRelative
- static $withProtRel = null, $withoutProtRel = null;
- $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
- if ( !is_null( $cachedValue ) ) {
- return $cachedValue;
- }
- // Support old-style $wgUrlProtocols strings, for backwards compatibility
- // with LocalSettings files from 1.5
- if ( is_array( $wgUrlProtocols ) ) {
- $protocols = array();
- foreach ( $wgUrlProtocols as $protocol ) {
- // Filter out '//' if !$includeProtocolRelative
- if ( $includeProtocolRelative || $protocol !== '//' ) {
- $protocols[] = preg_quote( $protocol, '/' );
- }
- }
- $retval = implode( '|', $protocols );
- } else {
- // Ignore $includeProtocolRelative in this case
- // This case exists for pre-1.6 compatibility, and we can safely assume
- // that '//' won't appear in a pre-1.6 config because protocol-relative
- // URLs weren't supported until 1.18
- $retval = $wgUrlProtocols;
- }
- // Cache return value
- if ( $includeProtocolRelative ) {
- $withProtRel = $retval;
- } else {
- $withoutProtRel = $retval;
- }
- return $retval;
- }
- /**
- * Like wfUrlProtocols(), but excludes '//' from the protocol list. Use this if
- * you need a regex that matches all URL protocols but does not match protocol-
- * relative URLs
- * @return String
- */
- function wfUrlProtocolsWithoutProtRel() {
- return wfUrlProtocols( false );
- }
- /**
- * parse_url() work-alike, but non-broken. Differences:
- *
- * 1) Does not raise warnings on bad URLs (just returns false)
- * 2) Handles protocols that don't use :// (e.g., mailto: and news: , as well as protocol-relative URLs) correctly
- * 3) Adds a "delimiter" element to the array, either '://', ':' or '//' (see (2))
- *
- * @param $url String: a URL to parse
- * @return Array: bits of the URL in an associative array, per PHP docs
- */
- function wfParseUrl( $url ) {
- global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
- // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
- // way to handle them is to just prepend 'http:' and strip the protocol out later
- $wasRelative = substr( $url, 0, 2 ) == '//';
- if ( $wasRelative ) {
- $url = "http:$url";
- }
- wfSuppressWarnings();
- $bits = parse_url( $url );
- wfRestoreWarnings();
- // parse_url() returns an array without scheme for some invalid URLs, e.g.
- // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
- if ( !$bits || !isset( $bits['scheme'] ) ) {
- return false;
- }
- // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
- if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
- $bits['delimiter'] = '://';
- } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
- $bits['delimiter'] = ':';
- // parse_url detects for news: and mailto: the host part of an url as path
- // We have to correct this wrong detection
- if ( isset( $bits['path'] ) ) {
- $bits['host'] = $bits['path'];
- $bits['path'] = '';
- }
- } else {
- return false;
- }
- /* Provide an empty host for eg. file:/// urls (see bug 28627) */
- if ( !isset( $bits['host'] ) ) {
- $bits['host'] = '';
- /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
- if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
- $bits['path'] = '/' . $bits['path'];
- }
- }
- // If the URL was protocol-relative, fix scheme and delimiter
- if ( $wasRelative ) {
- $bits['scheme'] = '';
- $bits['delimiter'] = '//';
- }
- return $bits;
- }
- /**
- * Make URL indexes, appropriate for the el_index field of externallinks.
- *
- * @param $url String
- * @return array
- */
- function wfMakeUrlIndexes( $url ) {
- $bits = wfParseUrl( $url );
- // Reverse the labels in the hostname, convert to lower case
- // For emails reverse domainpart only
- if ( $bits['scheme'] == 'mailto' ) {
- $mailparts = explode( '@', $bits['host'], 2 );
- if ( count( $mailparts ) === 2 ) {
- $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
- } else {
- // No domain specified, don't mangle it
- $domainpart = '';
- }
- $reversedHost = $domainpart . '@' . $mailparts[0];
- } else {
- $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
- }
- // Add an extra dot to the end
- // Why? Is it in wrong place in mailto links?
- if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
- $reversedHost .= '.';
- }
- // Reconstruct the pseudo-URL
- $prot = $bits['scheme'];
- $index = $prot . $bits['delimiter'] . $reversedHost;
- // Leave out user and password. Add the port, path, query and fragment
- if ( isset( $bits['port'] ) ) {
- $index .= ':' . $bits['port'];
- }
- if ( isset( $bits['path'] ) ) {
- $index .= $bits['path'];
- } else {
- $index .= '/';
- }
- if ( isset( $bits['query'] ) ) {
- $index .= '?' . $bits['query'];
- }
- if ( isset( $bits['fragment'] ) ) {
- $index .= '#' . $bits['fragment'];
- }
- if ( $prot == '' ) {
- return array( "http:$index", "https:$index" );
- } else {
- return array( $index );
- }
- }
- /**
- * Check whether a given URL has a domain that occurs in a given set of domains
- * @param $url string URL
- * @param $domains array Array of domains (strings)
- * @return bool True if the host part of $url ends in one of the strings in $domains
- */
- function wfMatchesDomainList( $url, $domains ) {
- $bits = wfParseUrl( $url );
- if ( is_array( $bits ) && isset( $bits['host'] ) ) {
- foreach ( (array)$domains as $domain ) {
- // FIXME: This gives false positives. http://nds-nl.wikipedia.org will match nl.wikipedia.org
- // We should use something that interprets dots instead
- if ( substr( $bits['host'], -strlen( $domain ) ) === $domain ) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * 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 $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;
- global $wgDebugLogPrefix, $wgShowDebug;
- static $cache = array(); // Cache of unoutputted messages
- $text = wfDebugTimer() . $text;
- if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
- return;
- }
- if ( ( $wgDebugComments || $wgShowDebug ) && !$logonly ) {
- $cache[] = $text;
- if ( isset( $wgOut ) && is_object( $wgOut ) ) {
- // add the message and any cached messages to the output
- array_map( array( $wgOut, 'debug' ), $cache );
- $cache = array();
- }
- }
- if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) {
- if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
- # Strip unprintables; they can switch terminal modes when binary data
- # gets dumped, which is pretty annoying.
- $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- $text = $wgDebugLogPrefix . $text;
- wfErrorLog( $text, $wgDebugLogFile );
- }
- }
- MWDebug::debugMsg( $text );
- }
- /**
- * Returns true if debug logging should be suppressed if $wgDebugRawPage = false
- */
- function wfIsDebugRawPage() {
- static $cache;
- if ( $cache !== null ) {
- return $cache;
- }
- # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
- if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
- || (
- isset( $_SERVER['SCRIPT_NAME'] )
- && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
- ) )
- {
- $cache = true;
- } else {
- $cache = false;
- }
- return $cache;
- }
- /**
- * Get microsecond timestamps for debug logs
- *
- * @return string
- */
- function wfDebugTimer() {
- global $wgDebugTimestamps, $wgRequestTime;
- if ( !$wgDebugTimestamps ) {
- return '';
- }
- $prefix = sprintf( "%6.4f", microtime( true ) - $wgRequestTime );
- $mem = sprintf( "%5.1fM", ( memory_get_usage( true ) / ( 1024 * 1024 ) ) );
- return "$prefix $mem ";
- }
- /**
- * Send a line giving PHP memory usage.
- *
- * @param $exact Bool: print exact values instead of kilobytes (default: false)
- */
- function wfDebugMem( $exact = false ) {
- $mem = memory_get_usage();
- if( !$exact ) {
- $mem = floor( $mem / 1024 ) . ' kilobytes';
- } else {
- $mem .= ' bytes';
- }
- wfDebug( "Memory usage: $mem\n" );
- }
- /**
- * 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 $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;
- $text = trim( $text ) . "\n";
- if( isset( $wgDebugLogGroups[$logGroup] ) ) {
- $time = wfTimestamp( TS_DB );
- $wiki = wfWikiID();
- $host = wfHostname();
- if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) {
- wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
- }
- } elseif ( $public === true ) {
- wfDebug( $text, true );
- }
- }
- /**
- * Log for database errors
- *
- * @param $text String: database error message.
- */
- function wfLogDBError( $text ) {
- global $wgDBerrorLog;
- if ( $wgDBerrorLog ) {
- $host = wfHostname();
- $wiki = wfWikiID();
- $text = date( 'D M j G:i:s T Y' ) . "\t$host\t$wiki\t$text";
- wfErrorLog( $text, $wgDBerrorLog );
- }
- }
- /**
- * Throws a warning that $function is deprecated
- *
- * @param $function String
- * @param $version String|false: Added in 1.19.
- * @param $component String|false: Added in 1.19.
- *
- * @return null
- */
- function wfDeprecated( $function, $version = false, $component = false ) {
- static $functionsWarned = array();
- MWDebug::deprecated( $function, $version, $component );
- if ( !isset( $functionsWarned[$function] ) ) {
- $functionsWarned[$function] = true;
-
- if ( $version ) {
- global $wgDeprecationReleaseLimit;
-
- if ( $wgDeprecationReleaseLimit && $component === false ) {
- # Strip -* off the end of $version so that branches can use the
- # format #.##-branchname to avoid issues if the branch is merged into
- # a version of MediaWiki later than what it was branched from
- $comparableVersion = preg_replace( '/-.*$/', '', $version );
-
- # If the comparableVersion is larger than our release limit then
- # skip the warning message for the deprecation
- if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) {
- return;
- }
- }
-
- $component = $component === false ? 'MediaWiki' : $component;
- wfWarn( "Use of $function was deprecated in $component $version.", 2 );
- } else {
- wfWarn( "Use of $function is deprecated.", 2 );
- }
- }
- }
- /**
- * Send a warning either to the debug log or in a PHP error depending on
- * $wgDevelopmentWarnings
- *
- * @param $msg String: message to send
- * @param $callerOffset Integer: number of items to go back in the backtrace to
- * find the correct caller (1 = function calling wfWarn, ...)
- * @param $level Integer: PHP error level; only used when $wgDevelopmentWarnings
- * is true
- */
- function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
- global $wgDevelopmentWarnings;
- MWDebug::warning( $msg, $callerOffset + 2 );
- $callers = wfDebugBacktrace();
- if ( isset( $callers[$callerOffset + 1] ) ) {
- $callerfunc = $callers[$callerOffset + 1];
- $callerfile = $callers[$callerOffset];
- if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) {
- $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
- } else {
- $file = '(internal function)';
- }
- $func = '';
- if ( isset( $callerfunc['class'] ) ) {
- $func .= $callerfunc['class'] . '::';
- }
- if ( isset( $callerfunc['function'] ) ) {
- $func .= $callerfunc['function'];
- }
- $msg .= " [Called from $func in $file]";
- }
- if ( $wgDevelopmentWarnings ) {
- trigger_error( $msg, $level );
- } else {
- wfDebug( "$msg\n" );
- }
- }
- /**
- * Log to a file without getting "file size exceeded" signals.
- *
- * Can also log to TCP or UDP with the syntax udp://host:port/prefix. This will
- * send lines to the specified port, prefixed by the specified prefix and a space.
- *
- * @param $text String
- * @param $file String filename
- */
- function wfErrorLog( $text, $file ) {
- if ( substr( $file, 0, 4 ) == 'udp:' ) {
- # Needs the sockets extension
- if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
- // IPv6 bracketed host
- $host = $m[2];
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET6;
- } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
- $host = $m[2];
- if ( !IP::isIPv4( $host ) ) {
- $host = gethostbyname( $host );
- }
- $port = intval( $m[3] );
- $prefix = isset( $m[4] ) ? $m[4] : false;
- $domain = AF_INET;
- } 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 ) > 65506 ) {
- $text = substr( $text, 0, 65506 );
- }
- if ( substr( $text, -1 ) != "\n" ) {
- $text .= "\n";
- }
- } elseif ( strlen( $text ) > 65507 ) {
- $text = substr( $text, 0, 65507 );
- }
- $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
- if ( !$sock ) {
- return;
- }
- socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
- socket_close( $sock );
- } else {
- wfSuppressWarnings();
- $exists = file_exists( $file );
- $size = $exists ? filesize( $file ) : false;
- if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
- file_put_contents( $file, $text, FILE_APPEND );
- }
- wfRestoreWarnings();
- }
- }
- /**
- * @todo document
- */
- function wfLogProfilingData() {
- global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
- global $wgProfileLimit, $wgUser;
- $profiler = Profiler::instance();
- # Profiling must actually be enabled...
- if ( $profiler->isStub() ) {
- return;
- }
- // Get total page request time and only show pages that longer than
- // $wgProfileLimit time (default is 0)
- $elapsed = microtime( true ) - $wgRequestTime;
- if ( $elapsed <= $wgProfileLimit ) {
- return;
- }
- $profiler->logData();
- // Check whether this should be logged in the debug file.
- if ( $wgDebugLogFile == '' || ( !$wgDebugRawPage && wfIsDebugRawPage() ) ) {
- return;
- }
- $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})";
- }
- // Don't load $wgUser at this late stage just for statistics purposes
- // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId()
- if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) {
- $forward .= ' anon';
- }
- $log = sprintf( "%s\t%04.3f\t%s\n",
- gmdate( 'YmdHis' ), $elapsed,
- urldecode( $wgRequest->getRequestURL() . $forward ) );
- wfErrorLog( $log . $profiler->getOutput(), $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, $wgReadOnly;
- if ( !is_null( $wgReadOnly ) ) {
- return (bool)$wgReadOnly;
- }
- if ( $wgReadOnlyFile == '' ) {
- return false;
- }
- // Set $wgReadOnly for faster access next time
- if ( is_file( $wgReadOnlyFile ) ) {
- $wgReadOnly = file_get_contents( $wgReadOnlyFile );
- } else {
- $wgReadOnly = false;
- }
- return (bool)$wgReadOnly;
- }
- /**
- * @return bool
- */
- function wfReadOnlyReason() {
- global $wgReadOnly;
- wfReadOnly();
- return $wgReadOnly;
- }
- /**
- * Return a Language object from $langcode
- *
- * @param $langcode Mixed: either:
- * - a Language object
- * - code of the language to get the message for, if it is
- * a valid code create a language for that language, if
- * it is a string but not a valid code then make a basic
- * language object
- * - a boolean: if it's false then use the global object for
- * the current user's language (as a fallback for the old parameter
- * functionality), or if it is true then use global object
- * for the wiki's content language.
- * @return Language object
- */
- function wfGetLangObj( $langcode = false ) {
- # Identify which language to get or create a language object for.
- # Using is_object here due to Stub objects.
- if( is_object( $langcode ) ) {
- # Great, we already have the object (hopefully)!
- return $langcode;
- }
- global $wgContLang, $wgLanguageCode;
- if( $langcode === true || $langcode === $wgLanguageCode ) {
- # $langcode is the language code of the wikis content language object.
- # or it is a boolean and value is true
- return $wgContLang;
- }
- global $wgLang;
- if( $langcode === false || $langcode === $wgLang->getCode() ) {
- # $langcode is the language code of user language object.
- # or it was a boolean and value is false
- return $wgLang;
- }
- $validCodes = array_keys( Language::getLanguageNames() );
- if( in_array( $langcode, $validCodes ) ) {
- # $langcode corresponds to a valid language.
- return Language::factory( $langcode );
- }
- # $langcode is a string, but not a valid language code; use content language.
- wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
- return $wgContLang;
- }
- /**
- * Old function when $wgBetterDirectionality existed
- * Removed in core, kept in extensions for backwards compat.
- *
- * @deprecated since 1.18
- * @return Language
- */
- function wfUILang() {
- wfDeprecated( __METHOD__, '1.18' );
- global $wgLang;
- return $wgLang;
- }
- /**
- * This is the new function for getting translated interface messages.
- * See the Message class for documentation how to use them.
- * The intention is that this function replaces all old wfMsg* functions.
- * @param $key \string Message key.
- * Varargs: normal message parameters.
- * @return Message
- * @since 1.17
- */
- function wfMessage( $key /*...*/) {
- $params = func_get_args();
- array_shift( $params );
- if ( isset( $params[0] ) && is_array( $params[0] ) ) {
- $params = $params[0];
- }
- 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.
- * @param varargs: message keys
- * @return Message
- * @since 1.18
- */
- function wfMessageFallback( /*...*/ ) {
- $args = func_get_args();
- return MWFunction::callArray( 'Message::newFallbackSequence', $args );
- }
- /**
- * Get a message from anywhere, for the current user language.
- *
- * Use wfMsgForContent() instead if the message should NOT
- * change depending on the user preferences.
- *
- * @param $key String: lookup key for the message, usually
- * defined in languages/Language.php
- *
- * Parameters to the message, which can be used to insert variable text into
- * it, can be passed to this function in the following formats:
- * - One per argument, starting at the second parameter
- * - As an array in the second parameter
- * These are not shown in the function definition.
- *
- * @return String
- */
- function wfMsg( $key ) {
- $args = func_get_args();
- array_shift( $args );
- return wfMsgReal( $key, $args );
- }
- /**
- * Same as above except doesn't transform the message
- *
- * @param $key String
- * @return String
- */
- function wfMsgNoTrans( $key ) {
- $args = func_get_args();
- array_shift( $args );
- 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
- * 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
- * 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
- * customize potentially hundreds of messages in
- * order to, e.g., fix a link in every possible language.
- *
- * @param $key String: lookup key for the message, usually
- * defined in languages/Language.php
- * @return String
- */
- function wfMsgForContent( $key ) {
- global $wgForceUIMsgAsContentMsg;
- $args = func_get_args();
- array_shift( $args );
- $forcontent = true;
- if( is_array( $wgForceUIMsgAsContentMsg ) &&
- in_array( $key, $wgForceUIMsgAsContentMsg ) )
- {
- $forcontent = false;
- }
- return wfMsgReal( $key, $args, true, $forcontent );
- }
- /**
- * Same as above except doesn't transform the message
- *
- * @param $key String
- * @return String
- */
- function wfMsgForContentNoTrans( $key ) {
- global $wgForceUIMsgAsContentMsg;
- $args = func_get_args();
- array_shift( $args );
- $forcontent = true;
- if( is_array( $wgForceUIMsgAsContentMsg ) &&
- in_array( $key, $wgForceUIMsgAsContentMsg ) )
- {
- $forcontent = false;
- }
- return wfMsgReal( $key, $args, true, $forcontent, false );
- }
- /**
- * Really get a message
- *
- * @param $key String: key to get.
- * @param $args
- * @param $useDB Boolean
- * @param $forContent Mixed: Language code, or false for user lang, true for content lang.
- * @param $transform Boolean: Whether or not to transform the message.
- * @return String: the requested message.
- */
- function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
- wfProfileIn( __METHOD__ );
- $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
- $message = wfMsgReplaceArgs( $message, $args );
- wfProfileOut( __METHOD__ );
- return $message;
- }
- /**
- * Fetch a message string value, but don't replace any keys yet.
- *
- * @param $key String
- * @param $useDB Bool
- * @param $langCode String: Code of the language to get the message for, or
- * behaves as a content language switch if it is a boolean.
- * @param $transform Boolean: whether to parse magic words, etc.
- * @return string
- */
- function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
- wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
- $cache = MessageCache::singleton();
- $message = $cache->get( $key, $useDB, $langCode );
- if( $message === false ) {
- $message = '<' . htmlspecialchars( $key ) . '>';
- } elseif ( $transform ) {
- $message = $cache->transform( $message );
- }
- return $message;
- }
- /**
- * Replace message parameter keys on the given formatted output.
- *
- * @param $message String
- * @param $args Array
- * @return string
- * @private
- */
- function wfMsgReplaceArgs( $message, $args ) {
- # Fix windows line-endings
- # Some messages are split with explode("\n", $msg)
- $message = str_replace( "\r", '', $message );
- // Replace arguments
- if ( count( $args ) ) {
- if ( is_array( $args[0] ) ) {
- $args = array_values( $args[0] );
- }
- $replacementKeys = array();
- foreach( $args as $n => $param ) {
- $replacementKeys['$' . ( $n + 1 )] = $param;
- }
- $message = strtr( $message, $replacementKeys );
- }
- return $message;
- }
- /**
- * Return an HTML-escaped version of a message.
- * Parameter replacements, if any, are done *after* the HTML-escaping,
- * so parameters may contain HTML (eg links or form controls). Be sure
- * to pre-escape them if you really do want plaintext, or just wrap
- * the whole thing in htmlspecialchars().
- *
- * @param $key String
- * @param string ... parameters
- * @return string
- */
- function wfMsgHtml( $key ) {
- $args = func_get_args();
- array_shift( $args );
- return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args );
- }
- /**
- * Return an HTML version of message
- * Parameter replacements, if any, are done *after* parsing the wiki-text message,
- * so parameters may contain HTML (eg links or form controls). Be sure
- * to pre-escape them if you really do want plaintext, or just wrap
- * the whole thing in htmlspecialchars().
- *
- * @param $key String
- * @param string ... parameters
- * @return string
- */
- function wfMsgWikiHtml( $key ) {
- $args = func_get_args();
- array_shift( $args );
- return wfMsgReplaceArgs(
- MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
- /* can't be set to false */ true, /* interface */ true )->getText(),
- $args );
- }
- /**
- * Returns message in the requested format
- * @param $key String: key of the message
- * @param $options Array: processing rules. Can take the following options:
- * <i>parse</i>: parses wikitext to HTML
- * <i>parseinline</i>: parses wikitext to HTML and removes the surrounding
- * p's added by parser or tidy
- * <i>escape</i>: filters message through htmlspecialchars
- * <i>escapenoentities</i>: same, but allows entity references like   through
- * <i>replaceafter</i>: parameters are substituted after parsing or escaping
- * <i>parsemag</i>: transform the message using magic phrases
- * <i>content</i>: fetch message for content language instead of interface
- * Also can accept a single associative argument, of the form 'language' => 'xx':
- * <i>language</i>: Language object or language code to fetch message for
- * (overriden by <i>content</i>).
- * Behavior for conflicting options (e.g., parse+parseinline) is undefined.
- *
- * @return String
- */
- function wfMsgExt( $key, $options ) {
- $args = func_get_args();
- array_shift( $args );
- array_shift( $args );
- $options = (array)$options;
- foreach( $options as $arrayKey => $option ) {
- if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
- # An unknown index, neither numeric nor "language"
- wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
- } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
- array( 'parse', 'parseinline', 'escape', 'escapenoentities',
- 'replaceafter', 'parsemag', 'content' ) ) ) {
- # A numeric index with unknown value
- wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
- }
- }
- 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 );
- if( !in_array( 'replaceafter', $options, true ) ) {
- $string = wfMsgReplaceArgs( $string, $args );
- }
- $messageCache = MessageCache::singleton();
- $parseInline = in_array( 'parseinline', $options, true );
- if( in_array( 'parse', $options, true ) || $parseInline ) {
- $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
- if ( $string instanceof ParserOutput ) {
- $string = $string->getText();
- }
- if ( $parseInline ) {
- $m = array();
- if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
- $string = $m[1];
- }
- }
- } elseif ( in_array( 'parsemag', $options, true ) ) {
- $string = $messageCache->transform( $string,
- !$forContent, $langCodeObj );
- }
- if ( in_array( 'escape', $options, true ) ) {
- $string = htmlspecialchars ( $string );
- } elseif ( in_array( 'escapenoentities', $options, true ) ) {
- $string = Sanitizer::escapeHtmlAllowEntities( $string );
- }
- if( in_array( 'replaceafter', $options, true ) ) {
- $string = wfMsgReplaceArgs( $string, $args );
- }
- return $string;
- }
- /**
- * Since wfMsg() and co suck, they don't return false if the message key they
- * looked up didn't exist but a XHTML string, this function checks for the
- * nonexistance of messages by checking the MessageCache::get() result directly.
- *
- * @param $key String: the message key looked up
- * @return Boolean True if the message *doesn't* exist.
- */
- function wfEmptyMsg( $key ) {
- return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false;
- }
- /**
- * Throw a debugging exception. This function previously once exited the process,
- * but now throws an exception instead, with similar results.
- *
- * @param $msg String: message shown when dying.
- */
- function wfDebugDieBacktrace( $msg = '' ) {
- throw new MWException( $msg );
- }
- /**
- * 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() {
- static $host;
- if ( is_null( $host ) ) {
- if ( function_exists( 'posix_uname' ) ) {
- // This function not present on Windows
- $uname = posix_uname();
- } else {
- $uname = false;
- }
- if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
- $host = $uname['nodename'];
- } elseif ( getenv( 'COMPUTERNAME' ) ) {
- # Windows computer name
- $host = getenv( 'COMPUTERNAME' );
- } else {
- # This may be a virtual server.
- $host = $_SERVER['SERVER_NAME'];
- }
- }
- return $host;
- }
- /**
- * Returns a HTML comment with the elapsed time since request.
- * This method has no side effects.
- *
- * @return string
- */
- function wfReportTime() {
- global $wgRequestTime, $wgShowHostnames;
- $elapsed = microtime( true ) - $wgRequestTime;
- return $wgShowHostnames
- ? sprintf( '<!-- Served by %s in %01.3f secs. -->', wfHostname(), $elapsed )
- : sprintf( '<!-- Served in %01.3f secs. -->', $elapsed );
- }
- /**
- * Safety wrapper for debug_backtrace().
- *
- * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat
- * murky circumstances, which may be triggered in part by stub objects
- * or other fancy talkin'.
- *
- * Will return an empty array if Zend Optimizer is detected or if
- * debug_backtrace is disabled, otherwise the output from
- * debug_backtrace() (trimmed).
- *
- * @param $limit int This parameter can be used to limit the number of stack frames returned
- *
- * @return array of backtrace information
- */
- function wfDebugBacktrace( $limit = 0 ) {
- static $disabled = null;
- if( extension_loaded( 'Zend Optimizer' ) ) {
- wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
- return array();
- }
- if ( is_null( $disabled ) ) {
- $disabled = false;
- $functions = explode( ',', ini_get( 'disable_functions' ) );
- $functions = array_map( 'trim', $functions );
- $functions = array_map( 'strtolower', $functions );
- if ( in_array( 'debug_backtrace', $functions ) ) {
- wfDebug( "debug_backtrace is in disabled_functions\n" );
- $disabled = true;
- }
- }
- if ( $disabled ) {
- return array();
- }
- if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) {
- return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit ), 1 );
- } else {
- return array_slice( debug_backtrace(), 1 );
- }
- }
- /**
- * Get a debug backtrace as a string
- *
- * @return string
- */
- function wfBacktrace() {
- global $wgCommandLineMode;
- if ( $wgCommandLineMode ) {
- $msg = '';
- } else {
- $msg = "<ul>\n";
- }
- $backtrace = wfDebugBacktr…
Large files files are truncated, but you can click here to view the full file