MediaWiki  REL1_24
GlobalFunctions.php
Go to the documentation of this file.
00001 <?php
00023 if ( !defined( 'MEDIAWIKI' ) ) {
00024     die( "This file is part of MediaWiki, it is not a valid entry point" );
00025 }
00026 
00027 // Hide compatibility functions from Doxygen
00029 
00038 if ( !function_exists( 'mb_substr' ) ) {
00044     function mb_substr( $str, $start, $count = 'end' ) {
00045         return Fallback::mb_substr( $str, $start, $count );
00046     }
00047 
00053     function mb_substr_split_unicode( $str, $splitPos ) {
00054         return Fallback::mb_substr_split_unicode( $str, $splitPos );
00055     }
00056 }
00057 
00058 if ( !function_exists( 'mb_strlen' ) ) {
00064     function mb_strlen( $str, $enc = '' ) {
00065         return Fallback::mb_strlen( $str, $enc );
00066     }
00067 }
00068 
00069 if ( !function_exists( 'mb_strpos' ) ) {
00075     function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
00076         return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding );
00077     }
00078 }
00079 
00080 if ( !function_exists( 'mb_strrpos' ) ) {
00086     function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
00087         return Fallback::mb_strrpos( $haystack, $needle, $offset, $encoding );
00088     }
00089 }
00090 
00091 // gzdecode function only exists in PHP >= 5.4.0
00092 // http://php.net/gzdecode
00093 if ( !function_exists( 'gzdecode' ) ) {
00099     function gzdecode( $data ) {
00100         return gzinflate( substr( $data, 10, -8 ) );
00101     }
00102 }
00103 
00104 // hash_equals function only exists in PHP >= 5.6.0
00105 if ( !function_exists( 'hash_equals' ) ) {
00121     function hash_equals( $known_string, $user_string ) {
00122         // Strict type checking as in PHP's native implementation
00123         if ( !is_string( $known_string ) ) {
00124             trigger_error( 'hash_equals(): Expected known_string to be a string, ' .
00125                 gettype( $known_string ) . ' given', E_USER_WARNING );
00126 
00127             return false;
00128         }
00129 
00130         if ( !is_string( $user_string ) ) {
00131             trigger_error( 'hash_equals(): Expected user_string to be a string, ' .
00132                 gettype( $user_string ) . ' given', E_USER_WARNING );
00133 
00134             return false;
00135         }
00136 
00137         // Note that we do one thing PHP doesn't: try to avoid leaking information about
00138         // relative lengths of $known_string and $user_string, and of multiple $known_strings.
00139         // However, lengths may still inevitably leak through, for example, CPU cache misses.
00140         $known_string_len = strlen( $known_string );
00141         $user_string_len = strlen( $user_string );
00142         $result = $known_string_len ^ $user_string_len;
00143         for ( $i = 0; $i < $user_string_len; $i++ ) {
00144             $result |= ord( $known_string[$i % $known_string_len] ) ^ ord( $user_string[$i] );
00145         }
00146 
00147         return ( $result === 0 );
00148     }
00149 }
00151 
00158 function wfArrayDiff2( $a, $b ) {
00159     return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
00160 }
00161 
00167 function wfArrayDiff2_cmp( $a, $b ) {
00168     if ( is_string( $a ) && is_string( $b ) ) {
00169         return strcmp( $a, $b );
00170     } elseif ( count( $a ) !== count( $b ) ) {
00171         return count( $a ) < count( $b ) ? -1 : 1;
00172     } else {
00173         reset( $a );
00174         reset( $b );
00175         while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
00176             $cmp = strcmp( $valueA, $valueB );
00177             if ( $cmp !== 0 ) {
00178                 return $cmp;
00179             }
00180         }
00181         return 0;
00182     }
00183 }
00184 
00194 function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
00195     if ( is_null( $changed ) ) {
00196         throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
00197     }
00198     if ( $default[$key] !== $value ) {
00199         $changed[$key] = $value;
00200     }
00201 }
00202 
00222 function wfMergeErrorArrays( /*...*/ ) {
00223     $args = func_get_args();
00224     $out = array();
00225     foreach ( $args as $errors ) {
00226         foreach ( $errors as $params ) {
00227             # @todo FIXME: Sometimes get nested arrays for $params,
00228             # which leads to E_NOTICEs
00229             $spec = implode( "\t", $params );
00230             $out[$spec] = $params;
00231         }
00232     }
00233     return array_values( $out );
00234 }
00235 
00244 function wfArrayInsertAfter( array $array, array $insert, $after ) {
00245     // Find the offset of the element to insert after.
00246     $keys = array_keys( $array );
00247     $offsetByKey = array_flip( $keys );
00248 
00249     $offset = $offsetByKey[$after];
00250 
00251     // Insert at the specified offset
00252     $before = array_slice( $array, 0, $offset + 1, true );
00253     $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
00254 
00255     $output = $before + $insert + $after;
00256 
00257     return $output;
00258 }
00259 
00267 function wfObjectToArray( $objOrArray, $recursive = true ) {
00268     $array = array();
00269     if ( is_object( $objOrArray ) ) {
00270         $objOrArray = get_object_vars( $objOrArray );
00271     }
00272     foreach ( $objOrArray as $key => $value ) {
00273         if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
00274             $value = wfObjectToArray( $value );
00275         }
00276 
00277         $array[$key] = $value;
00278     }
00279 
00280     return $array;
00281 }
00282 
00290 function wfRandom() {
00291     # The maximum random value is "only" 2^31-1, so get two random
00292     # values to reduce the chance of dupes
00293     $max = mt_getrandmax() + 1;
00294     $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' );
00295 
00296     return $rand;
00297 }
00298 
00309 function wfRandomString( $length = 32 ) {
00310     $str = '';
00311     for ( $n = 0; $n < $length; $n += 7 ) {
00312         $str .= sprintf( '%07x', mt_rand() & 0xfffffff );
00313     }
00314     return substr( $str, 0, $length );
00315 }
00316 
00339 function wfUrlencode( $s ) {
00340     static $needle;
00341 
00342     if ( is_null( $s ) ) {
00343         $needle = null;
00344         return '';
00345     }
00346 
00347     if ( is_null( $needle ) ) {
00348         $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
00349         if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) ||
00350             ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false )
00351         ) {
00352             $needle[] = '%3A';
00353         }
00354     }
00355 
00356     $s = urlencode( $s );
00357     $s = str_ireplace(
00358         $needle,
00359         array( ';', '@', '$', '!', '*', '(', ')', ',', '/', ':' ),
00360         $s
00361     );
00362 
00363     return $s;
00364 }
00365 
00376 function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) {
00377     if ( !is_null( $array2 ) ) {
00378         $array1 = $array1 + $array2;
00379     }
00380 
00381     $cgi = '';
00382     foreach ( $array1 as $key => $value ) {
00383         if ( !is_null( $value ) && $value !== false ) {
00384             if ( $cgi != '' ) {
00385                 $cgi .= '&';
00386             }
00387             if ( $prefix !== '' ) {
00388                 $key = $prefix . "[$key]";
00389             }
00390             if ( is_array( $value ) ) {
00391                 $firstTime = true;
00392                 foreach ( $value as $k => $v ) {
00393                     $cgi .= $firstTime ? '' : '&';
00394                     if ( is_array( $v ) ) {
00395                         $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" );
00396                     } else {
00397                         $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
00398                     }
00399                     $firstTime = false;
00400                 }
00401             } else {
00402                 if ( is_object( $value ) ) {
00403                     $value = $value->__toString();
00404                 }
00405                 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
00406             }
00407         }
00408     }
00409     return $cgi;
00410 }
00411 
00421 function wfCgiToArray( $query ) {
00422     if ( isset( $query[0] ) && $query[0] == '?' ) {
00423         $query = substr( $query, 1 );
00424     }
00425     $bits = explode( '&', $query );
00426     $ret = array();
00427     foreach ( $bits as $bit ) {
00428         if ( $bit === '' ) {
00429             continue;
00430         }
00431         if ( strpos( $bit, '=' ) === false ) {
00432             // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
00433             $key = $bit;
00434             $value = '';
00435         } else {
00436             list( $key, $value ) = explode( '=', $bit );
00437         }
00438         $key = urldecode( $key );
00439         $value = urldecode( $value );
00440         if ( strpos( $key, '[' ) !== false ) {
00441             $keys = array_reverse( explode( '[', $key ) );
00442             $key = array_pop( $keys );
00443             $temp = $value;
00444             foreach ( $keys as $k ) {
00445                 $k = substr( $k, 0, -1 );
00446                 $temp = array( $k => $temp );
00447             }
00448             if ( isset( $ret[$key] ) ) {
00449                 $ret[$key] = array_merge( $ret[$key], $temp );
00450             } else {
00451                 $ret[$key] = $temp;
00452             }
00453         } else {
00454             $ret[$key] = $value;
00455         }
00456     }
00457     return $ret;
00458 }
00459 
00468 function wfAppendQuery( $url, $query ) {
00469     if ( is_array( $query ) ) {
00470         $query = wfArrayToCgi( $query );
00471     }
00472     if ( $query != '' ) {
00473         if ( false === strpos( $url, '?' ) ) {
00474             $url .= '?';
00475         } else {
00476             $url .= '&';
00477         }
00478         $url .= $query;
00479     }
00480     return $url;
00481 }
00482 
00506 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
00507     global $wgServer, $wgCanonicalServer, $wgInternalServer, $wgRequest,
00508         $wgHttpsPort;
00509     if ( $defaultProto === PROTO_CANONICAL ) {
00510         $serverUrl = $wgCanonicalServer;
00511     } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
00512         // Make $wgInternalServer fall back to $wgServer if not set
00513         $serverUrl = $wgInternalServer;
00514     } else {
00515         $serverUrl = $wgServer;
00516         if ( $defaultProto === PROTO_CURRENT ) {
00517             $defaultProto = $wgRequest->getProtocol() . '://';
00518         }
00519     }
00520 
00521     // Analyze $serverUrl to obtain its protocol
00522     $bits = wfParseUrl( $serverUrl );
00523     $serverHasProto = $bits && $bits['scheme'] != '';
00524 
00525     if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
00526         if ( $serverHasProto ) {
00527             $defaultProto = $bits['scheme'] . '://';
00528         } else {
00529             // $wgCanonicalServer or $wgInternalServer doesn't have a protocol.
00530             // This really isn't supposed to happen. Fall back to HTTP in this
00531             // ridiculous case.
00532             $defaultProto = PROTO_HTTP;
00533         }
00534     }
00535 
00536     $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
00537 
00538     if ( substr( $url, 0, 2 ) == '//' ) {
00539         $url = $defaultProtoWithoutSlashes . $url;
00540     } elseif ( substr( $url, 0, 1 ) == '/' ) {
00541         // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes,
00542         // otherwise leave it alone.
00543         $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
00544     }
00545 
00546     $bits = wfParseUrl( $url );
00547 
00548     // ensure proper port for HTTPS arrives in URL
00549     // https://bugzilla.wikimedia.org/show_bug.cgi?id=65184
00550     if ( $defaultProto === PROTO_HTTPS && $wgHttpsPort != 443 ) {
00551         $bits['port'] = $wgHttpsPort;
00552     }
00553 
00554     if ( $bits && isset( $bits['path'] ) ) {
00555         $bits['path'] = wfRemoveDotSegments( $bits['path'] );
00556         return wfAssembleUrl( $bits );
00557     } elseif ( $bits ) {
00558         # No path to expand
00559         return $url;
00560     } elseif ( substr( $url, 0, 1 ) != '/' ) {
00561         # URL is a relative path
00562         return wfRemoveDotSegments( $url );
00563     }
00564 
00565     # Expanded URL is not valid.
00566     return false;
00567 }
00568 
00582 function wfAssembleUrl( $urlParts ) {
00583     $result = '';
00584 
00585     if ( isset( $urlParts['delimiter'] ) ) {
00586         if ( isset( $urlParts['scheme'] ) ) {
00587             $result .= $urlParts['scheme'];
00588         }
00589 
00590         $result .= $urlParts['delimiter'];
00591     }
00592 
00593     if ( isset( $urlParts['host'] ) ) {
00594         if ( isset( $urlParts['user'] ) ) {
00595             $result .= $urlParts['user'];
00596             if ( isset( $urlParts['pass'] ) ) {
00597                 $result .= ':' . $urlParts['pass'];
00598             }
00599             $result .= '@';
00600         }
00601 
00602         $result .= $urlParts['host'];
00603 
00604         if ( isset( $urlParts['port'] ) ) {
00605             $result .= ':' . $urlParts['port'];
00606         }
00607     }
00608 
00609     if ( isset( $urlParts['path'] ) ) {
00610         $result .= $urlParts['path'];
00611     }
00612 
00613     if ( isset( $urlParts['query'] ) ) {
00614         $result .= '?' . $urlParts['query'];
00615     }
00616 
00617     if ( isset( $urlParts['fragment'] ) ) {
00618         $result .= '#' . $urlParts['fragment'];
00619     }
00620 
00621     return $result;
00622 }
00623 
00634 function wfRemoveDotSegments( $urlPath ) {
00635     $output = '';
00636     $inputOffset = 0;
00637     $inputLength = strlen( $urlPath );
00638 
00639     while ( $inputOffset < $inputLength ) {
00640         $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
00641         $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
00642         $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
00643         $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
00644         $trimOutput = false;
00645 
00646         if ( $prefixLengthTwo == './' ) {
00647             # Step A, remove leading "./"
00648             $inputOffset += 2;
00649         } elseif ( $prefixLengthThree == '../' ) {
00650             # Step A, remove leading "../"
00651             $inputOffset += 3;
00652         } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
00653             # Step B, replace leading "/.$" with "/"
00654             $inputOffset += 1;
00655             $urlPath[$inputOffset] = '/';
00656         } elseif ( $prefixLengthThree == '/./' ) {
00657             # Step B, replace leading "/./" with "/"
00658             $inputOffset += 2;
00659         } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
00660             # Step C, replace leading "/..$" with "/" and
00661             # remove last path component in output
00662             $inputOffset += 2;
00663             $urlPath[$inputOffset] = '/';
00664             $trimOutput = true;
00665         } elseif ( $prefixLengthFour == '/../' ) {
00666             # Step C, replace leading "/../" with "/" and
00667             # remove last path component in output
00668             $inputOffset += 3;
00669             $trimOutput = true;
00670         } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
00671             # Step D, remove "^.$"
00672             $inputOffset += 1;
00673         } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
00674             # Step D, remove "^..$"
00675             $inputOffset += 2;
00676         } else {
00677             # Step E, move leading path segment to output
00678             if ( $prefixLengthOne == '/' ) {
00679                 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
00680             } else {
00681                 $slashPos = strpos( $urlPath, '/', $inputOffset );
00682             }
00683             if ( $slashPos === false ) {
00684                 $output .= substr( $urlPath, $inputOffset );
00685                 $inputOffset = $inputLength;
00686             } else {
00687                 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
00688                 $inputOffset += $slashPos - $inputOffset;
00689             }
00690         }
00691 
00692         if ( $trimOutput ) {
00693             $slashPos = strrpos( $output, '/' );
00694             if ( $slashPos === false ) {
00695                 $output = '';
00696             } else {
00697                 $output = substr( $output, 0, $slashPos );
00698             }
00699         }
00700     }
00701 
00702     return $output;
00703 }
00704 
00712 function wfUrlProtocols( $includeProtocolRelative = true ) {
00713     global $wgUrlProtocols;
00714 
00715     // Cache return values separately based on $includeProtocolRelative
00716     static $withProtRel = null, $withoutProtRel = null;
00717     $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
00718     if ( !is_null( $cachedValue ) ) {
00719         return $cachedValue;
00720     }
00721 
00722     // Support old-style $wgUrlProtocols strings, for backwards compatibility
00723     // with LocalSettings files from 1.5
00724     if ( is_array( $wgUrlProtocols ) ) {
00725         $protocols = array();
00726         foreach ( $wgUrlProtocols as $protocol ) {
00727             // Filter out '//' if !$includeProtocolRelative
00728             if ( $includeProtocolRelative || $protocol !== '//' ) {
00729                 $protocols[] = preg_quote( $protocol, '/' );
00730             }
00731         }
00732 
00733         $retval = implode( '|', $protocols );
00734     } else {
00735         // Ignore $includeProtocolRelative in this case
00736         // This case exists for pre-1.6 compatibility, and we can safely assume
00737         // that '//' won't appear in a pre-1.6 config because protocol-relative
00738         // URLs weren't supported until 1.18
00739         $retval = $wgUrlProtocols;
00740     }
00741 
00742     // Cache return value
00743     if ( $includeProtocolRelative ) {
00744         $withProtRel = $retval;
00745     } else {
00746         $withoutProtRel = $retval;
00747     }
00748     return $retval;
00749 }
00750 
00757 function wfUrlProtocolsWithoutProtRel() {
00758     return wfUrlProtocols( false );
00759 }
00760 
00772 function wfParseUrl( $url ) {
00773     global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
00774 
00775     // Protocol-relative URLs are handled really badly by parse_url(). It's so
00776     // bad that the easiest way to handle them is to just prepend 'http:' and
00777     // strip the protocol out later.
00778     $wasRelative = substr( $url, 0, 2 ) == '//';
00779     if ( $wasRelative ) {
00780         $url = "http:$url";
00781     }
00782     wfSuppressWarnings();
00783     $bits = parse_url( $url );
00784     wfRestoreWarnings();
00785     // parse_url() returns an array without scheme for some invalid URLs, e.g.
00786     // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
00787     if ( !$bits || !isset( $bits['scheme'] ) ) {
00788         return false;
00789     }
00790 
00791     // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase.
00792     $bits['scheme'] = strtolower( $bits['scheme'] );
00793 
00794     // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
00795     if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
00796         $bits['delimiter'] = '://';
00797     } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
00798         $bits['delimiter'] = ':';
00799         // parse_url detects for news: and mailto: the host part of an url as path
00800         // We have to correct this wrong detection
00801         if ( isset( $bits['path'] ) ) {
00802             $bits['host'] = $bits['path'];
00803             $bits['path'] = '';
00804         }
00805     } else {
00806         return false;
00807     }
00808 
00809     /* Provide an empty host for eg. file:/// urls (see bug 28627) */
00810     if ( !isset( $bits['host'] ) ) {
00811         $bits['host'] = '';
00812 
00813         // bug 45069
00814         if ( isset( $bits['path'] ) ) {
00815             /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
00816             if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
00817                 $bits['path'] = '/' . $bits['path'];
00818             }
00819         } else {
00820             $bits['path'] = '';
00821         }
00822     }
00823 
00824     // If the URL was protocol-relative, fix scheme and delimiter
00825     if ( $wasRelative ) {
00826         $bits['scheme'] = '';
00827         $bits['delimiter'] = '//';
00828     }
00829     return $bits;
00830 }
00831 
00842 function wfExpandIRI( $url ) {
00843     return preg_replace_callback(
00844         '/((?:%[89A-F][0-9A-F])+)/i',
00845         'wfExpandIRI_callback',
00846         wfExpandUrl( $url )
00847     );
00848 }
00849 
00855 function wfExpandIRI_callback( $matches ) {
00856     return urldecode( $matches[1] );
00857 }
00858 
00865 function wfMakeUrlIndexes( $url ) {
00866     $bits = wfParseUrl( $url );
00867 
00868     // Reverse the labels in the hostname, convert to lower case
00869     // For emails reverse domainpart only
00870     if ( $bits['scheme'] == 'mailto' ) {
00871         $mailparts = explode( '@', $bits['host'], 2 );
00872         if ( count( $mailparts ) === 2 ) {
00873             $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
00874         } else {
00875             // No domain specified, don't mangle it
00876             $domainpart = '';
00877         }
00878         $reversedHost = $domainpart . '@' . $mailparts[0];
00879     } else {
00880         $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
00881     }
00882     // Add an extra dot to the end
00883     // Why? Is it in wrong place in mailto links?
00884     if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
00885         $reversedHost .= '.';
00886     }
00887     // Reconstruct the pseudo-URL
00888     $prot = $bits['scheme'];
00889     $index = $prot . $bits['delimiter'] . $reversedHost;
00890     // Leave out user and password. Add the port, path, query and fragment
00891     if ( isset( $bits['port'] ) ) {
00892         $index .= ':' . $bits['port'];
00893     }
00894     if ( isset( $bits['path'] ) ) {
00895         $index .= $bits['path'];
00896     } else {
00897         $index .= '/';
00898     }
00899     if ( isset( $bits['query'] ) ) {
00900         $index .= '?' . $bits['query'];
00901     }
00902     if ( isset( $bits['fragment'] ) ) {
00903         $index .= '#' . $bits['fragment'];
00904     }
00905 
00906     if ( $prot == '' ) {
00907         return array( "http:$index", "https:$index" );
00908     } else {
00909         return array( $index );
00910     }
00911 }
00912 
00919 function wfMatchesDomainList( $url, $domains ) {
00920     $bits = wfParseUrl( $url );
00921     if ( is_array( $bits ) && isset( $bits['host'] ) ) {
00922         $host = '.' . $bits['host'];
00923         foreach ( (array)$domains as $domain ) {
00924             $domain = '.' . $domain;
00925             if ( substr( $host, -strlen( $domain ) ) === $domain ) {
00926                 return true;
00927             }
00928         }
00929     }
00930     return false;
00931 }
00932 
00950 function wfDebug( $text, $dest = 'all' ) {
00951     global $wgDebugLogFile, $wgDebugRawPage, $wgDebugLogPrefix;
00952 
00953     if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
00954         return;
00955     }
00956 
00957     // Turn $dest into a string if it's a boolean (for b/c)
00958     if ( $dest === true ) {
00959         $dest = 'all';
00960     } elseif ( $dest === false ) {
00961         $dest = 'log';
00962     }
00963 
00964     $timer = wfDebugTimer();
00965     if ( $timer !== '' ) {
00966         $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 );
00967     }
00968 
00969     if ( $dest === 'all' ) {
00970         MWDebug::debugMsg( $text );
00971     }
00972 
00973     if ( $wgDebugLogFile != '' ) {
00974         # Strip unprintables; they can switch terminal modes when binary data
00975         # gets dumped, which is pretty annoying.
00976         $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
00977         $text = $wgDebugLogPrefix . $text;
00978         wfErrorLog( $text, $wgDebugLogFile );
00979     }
00980 }
00981 
00986 function wfIsDebugRawPage() {
00987     static $cache;
00988     if ( $cache !== null ) {
00989         return $cache;
00990     }
00991     # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
00992     if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
00993         || (
00994             isset( $_SERVER['SCRIPT_NAME'] )
00995             && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
00996         )
00997     ) {
00998         $cache = true;
00999     } else {
01000         $cache = false;
01001     }
01002     return $cache;
01003 }
01004 
01010 function wfDebugTimer() {
01011     global $wgDebugTimestamps, $wgRequestTime;
01012 
01013     if ( !$wgDebugTimestamps ) {
01014         return '';
01015     }
01016 
01017     $prefix = sprintf( "%6.4f", microtime( true ) - $wgRequestTime );
01018     $mem = sprintf( "%5.1fM", ( memory_get_usage( true ) / ( 1024 * 1024 ) ) );
01019     return "$prefix $mem  ";
01020 }
01021 
01027 function wfDebugMem( $exact = false ) {
01028     $mem = memory_get_usage();
01029     if ( !$exact ) {
01030         $mem = floor( $mem / 1024 ) . ' KiB';
01031     } else {
01032         $mem .= ' B';
01033     }
01034     wfDebug( "Memory usage: $mem\n" );
01035 }
01036 
01057 function wfDebugLog( $logGroup, $text, $dest = 'all' ) {
01058     global $wgDebugLogGroups;
01059 
01060     $text = trim( $text ) . "\n";
01061 
01062     // Turn $dest into a string if it's a boolean (for b/c)
01063     if ( $dest === true ) {
01064         $dest = 'all';
01065     } elseif ( $dest === false ) {
01066         $dest = 'private';
01067     }
01068 
01069     if ( !isset( $wgDebugLogGroups[$logGroup] ) ) {
01070         if ( $dest !== 'private' ) {
01071             wfDebug( "[$logGroup] $text", $dest );
01072         }
01073         return;
01074     }
01075 
01076     if ( $dest === 'all' ) {
01077         MWDebug::debugMsg( "[$logGroup] $text" );
01078     }
01079 
01080     $logConfig = $wgDebugLogGroups[$logGroup];
01081     if ( $logConfig === false ) {
01082         return;
01083     }
01084     if ( is_array( $logConfig ) ) {
01085         if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) {
01086             return;
01087         }
01088         $destination = $logConfig['destination'];
01089     } else {
01090         $destination = strval( $logConfig );
01091     }
01092 
01093     $time = wfTimestamp( TS_DB );
01094     $wiki = wfWikiID();
01095     $host = wfHostname();
01096     wfErrorLog( "$time $host $wiki: $text", $destination );
01097 }
01098 
01104 function wfLogDBError( $text ) {
01105     global $wgDBerrorLog, $wgDBerrorLogTZ;
01106     static $logDBErrorTimeZoneObject = null;
01107 
01108     if ( $wgDBerrorLog ) {
01109         $host = wfHostname();
01110         $wiki = wfWikiID();
01111 
01112         if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) {
01113             $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ );
01114         }
01115 
01116         // Workaround for https://bugs.php.net/bug.php?id=52063
01117         // Can be removed when min PHP > 5.3.2
01118         if ( $logDBErrorTimeZoneObject === null ) {
01119             $d = date_create( "now" );
01120         } else {
01121             $d = date_create( "now", $logDBErrorTimeZoneObject );
01122         }
01123 
01124         $date = $d->format( 'D M j G:i:s T Y' );
01125 
01126         $text = "$date\t$host\t$wiki\t" . trim( $text ) . "\n";
01127         wfErrorLog( $text, $wgDBerrorLog );
01128     }
01129 }
01130 
01144 function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) {
01145     MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 );
01146 }
01147 
01158 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
01159     MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' );
01160 }
01161 
01171 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) {
01172     MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' );
01173 }
01174 
01185 function wfErrorLog( $text, $file ) {
01186     if ( substr( $file, 0, 4 ) == 'udp:' ) {
01187         # Needs the sockets extension
01188         if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
01189             // IPv6 bracketed host
01190             $host = $m[2];
01191             $port = intval( $m[3] );
01192             $prefix = isset( $m[4] ) ? $m[4] : false;
01193             $domain = AF_INET6;
01194         } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
01195             $host = $m[2];
01196             if ( !IP::isIPv4( $host ) ) {
01197                 $host = gethostbyname( $host );
01198             }
01199             $port = intval( $m[3] );
01200             $prefix = isset( $m[4] ) ? $m[4] : false;
01201             $domain = AF_INET;
01202         } else {
01203             throw new MWException( __METHOD__ . ': Invalid UDP specification' );
01204         }
01205 
01206         // Clean it up for the multiplexer
01207         if ( strval( $prefix ) !== '' ) {
01208             $text = preg_replace( '/^/m', $prefix . ' ', $text );
01209 
01210             // Limit to 64KB
01211             if ( strlen( $text ) > 65506 ) {
01212                 $text = substr( $text, 0, 65506 );
01213             }
01214 
01215             if ( substr( $text, -1 ) != "\n" ) {
01216                 $text .= "\n";
01217             }
01218         } elseif ( strlen( $text ) > 65507 ) {
01219             $text = substr( $text, 0, 65507 );
01220         }
01221 
01222         $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
01223         if ( !$sock ) {
01224             return;
01225         }
01226 
01227         socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
01228         socket_close( $sock );
01229     } else {
01230         wfSuppressWarnings();
01231         $exists = file_exists( $file );
01232         $size = $exists ? filesize( $file ) : false;
01233         if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
01234             file_put_contents( $file, $text, FILE_APPEND );
01235         }
01236         wfRestoreWarnings();
01237     }
01238 }
01239 
01243 function wfLogProfilingData() {
01244     global $wgRequestTime, $wgDebugLogFile, $wgDebugLogGroups, $wgDebugRawPage;
01245     global $wgProfileLimit, $wgUser, $wgRequest;
01246 
01247     StatCounter::singleton()->flush();
01248 
01249     $profiler = Profiler::instance();
01250 
01251     # Profiling must actually be enabled...
01252     if ( $profiler->isStub() ) {
01253         return;
01254     }
01255 
01256     // Get total page request time and only show pages that longer than
01257     // $wgProfileLimit time (default is 0)
01258     $elapsed = microtime( true ) - $wgRequestTime;
01259     if ( $elapsed <= $wgProfileLimit ) {
01260         return;
01261     }
01262 
01263     $profiler->logData();
01264 
01265     // Check whether this should be logged in the debug file.
01266     if ( isset( $wgDebugLogGroups['profileoutput'] )
01267         && $wgDebugLogGroups['profileoutput'] === false
01268     ) {
01269         // Explicitely disabled
01270         return;
01271     }
01272     if ( !isset( $wgDebugLogGroups['profileoutput'] ) && $wgDebugLogFile == '' ) {
01273         // Logging not enabled; no point going further
01274         return;
01275     }
01276     if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
01277         return;
01278     }
01279 
01280     $forward = '';
01281     if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
01282         $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
01283     }
01284     if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
01285         $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
01286     }
01287     if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
01288         $forward .= ' from ' . $_SERVER['HTTP_FROM'];
01289     }
01290     if ( $forward ) {
01291         $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
01292     }
01293     // Don't load $wgUser at this late stage just for statistics purposes
01294     // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId()
01295     if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) {
01296         $forward .= ' anon';
01297     }
01298 
01299     // Command line script uses a FauxRequest object which does not have
01300     // any knowledge about an URL and throw an exception instead.
01301     try {
01302         $requestUrl = $wgRequest->getRequestURL();
01303     } catch ( MWException $e ) {
01304         $requestUrl = 'n/a';
01305     }
01306 
01307     $log = sprintf( "%s\t%04.3f\t%s\n",
01308         gmdate( 'YmdHis' ), $elapsed,
01309         urldecode( $requestUrl . $forward ) );
01310 
01311     wfDebugLog( 'profileoutput', $log . $profiler->getOutput() );
01312 }
01313 
01321 function wfIncrStats( $key, $count = 1 ) {
01322     StatCounter::singleton()->incr( $key, $count );
01323 }
01324 
01330 function wfReadOnly() {
01331     return wfReadOnlyReason() !== false;
01332 }
01333 
01339 function wfReadOnlyReason() {
01340     global $wgReadOnly, $wgReadOnlyFile;
01341 
01342     if ( $wgReadOnly === null ) {
01343         // Set $wgReadOnly for faster access next time
01344         if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) {
01345             $wgReadOnly = file_get_contents( $wgReadOnlyFile );
01346         } else {
01347             $wgReadOnly = false;
01348         }
01349     }
01350 
01351     return $wgReadOnly;
01352 }
01353 
01369 function wfGetLangObj( $langcode = false ) {
01370     # Identify which language to get or create a language object for.
01371     # Using is_object here due to Stub objects.
01372     if ( is_object( $langcode ) ) {
01373         # Great, we already have the object (hopefully)!
01374         return $langcode;
01375     }
01376 
01377     global $wgContLang, $wgLanguageCode;
01378     if ( $langcode === true || $langcode === $wgLanguageCode ) {
01379         # $langcode is the language code of the wikis content language object.
01380         # or it is a boolean and value is true
01381         return $wgContLang;
01382     }
01383 
01384     global $wgLang;
01385     if ( $langcode === false || $langcode === $wgLang->getCode() ) {
01386         # $langcode is the language code of user language object.
01387         # or it was a boolean and value is false
01388         return $wgLang;
01389     }
01390 
01391     $validCodes = array_keys( Language::fetchLanguageNames() );
01392     if ( in_array( $langcode, $validCodes ) ) {
01393         # $langcode corresponds to a valid language.
01394         return Language::factory( $langcode );
01395     }
01396 
01397     # $langcode is a string, but not a valid language code; use content language.
01398     wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
01399     return $wgContLang;
01400 }
01401 
01418 function wfMessage( $key /*...*/ ) {
01419     $params = func_get_args();
01420     array_shift( $params );
01421     if ( isset( $params[0] ) && is_array( $params[0] ) ) {
01422         $params = $params[0];
01423     }
01424     return new Message( $key, $params );
01425 }
01426 
01439 function wfMessageFallback( /*...*/ ) {
01440     $args = func_get_args();
01441     return call_user_func_array( 'Message::newFallbackSequence', $args );
01442 }
01443 
01463 function wfMsg( $key ) {
01464     wfDeprecated( __METHOD__, '1.21' );
01465 
01466     $args = func_get_args();
01467     array_shift( $args );
01468     return wfMsgReal( $key, $args );
01469 }
01470 
01479 function wfMsgNoTrans( $key ) {
01480     wfDeprecated( __METHOD__, '1.21' );
01481 
01482     $args = func_get_args();
01483     array_shift( $args );
01484     return wfMsgReal( $key, $args, true, false, false );
01485 }
01486 
01512 function wfMsgForContent( $key ) {
01513     wfDeprecated( __METHOD__, '1.21' );
01514 
01515     global $wgForceUIMsgAsContentMsg;
01516     $args = func_get_args();
01517     array_shift( $args );
01518     $forcontent = true;
01519     if ( is_array( $wgForceUIMsgAsContentMsg )
01520         && in_array( $key, $wgForceUIMsgAsContentMsg )
01521     ) {
01522         $forcontent = false;
01523     }
01524     return wfMsgReal( $key, $args, true, $forcontent );
01525 }
01526 
01535 function wfMsgForContentNoTrans( $key ) {
01536     wfDeprecated( __METHOD__, '1.21' );
01537 
01538     global $wgForceUIMsgAsContentMsg;
01539     $args = func_get_args();
01540     array_shift( $args );
01541     $forcontent = true;
01542     if ( is_array( $wgForceUIMsgAsContentMsg )
01543         && in_array( $key, $wgForceUIMsgAsContentMsg )
01544     ) {
01545         $forcontent = false;
01546     }
01547     return wfMsgReal( $key, $args, true, $forcontent, false );
01548 }
01549 
01562 function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
01563     wfDeprecated( __METHOD__, '1.21' );
01564 
01565     wfProfileIn( __METHOD__ );
01566     $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
01567     $message = wfMsgReplaceArgs( $message, $args );
01568     wfProfileOut( __METHOD__ );
01569     return $message;
01570 }
01571 
01584 function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
01585     wfDeprecated( __METHOD__, '1.21' );
01586 
01587     wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
01588 
01589     $cache = MessageCache::singleton();
01590     $message = $cache->get( $key, $useDB, $langCode );
01591     if ( $message === false ) {
01592         $message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
01593     } elseif ( $transform ) {
01594         $message = $cache->transform( $message );
01595     }
01596     return $message;
01597 }
01598 
01607 function wfMsgReplaceArgs( $message, $args ) {
01608     # Fix windows line-endings
01609     # Some messages are split with explode("\n", $msg)
01610     $message = str_replace( "\r", '', $message );
01611 
01612     // Replace arguments
01613     if ( count( $args ) ) {
01614         if ( is_array( $args[0] ) ) {
01615             $args = array_values( $args[0] );
01616         }
01617         $replacementKeys = array();
01618         foreach ( $args as $n => $param ) {
01619             $replacementKeys['$' . ( $n + 1 )] = $param;
01620         }
01621         $message = strtr( $message, $replacementKeys );
01622     }
01623 
01624     return $message;
01625 }
01626 
01640 function wfMsgHtml( $key ) {
01641     wfDeprecated( __METHOD__, '1.21' );
01642 
01643     $args = func_get_args();
01644     array_shift( $args );
01645     return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args );
01646 }
01647 
01661 function wfMsgWikiHtml( $key ) {
01662     wfDeprecated( __METHOD__, '1.21' );
01663 
01664     $args = func_get_args();
01665     array_shift( $args );
01666     return wfMsgReplaceArgs(
01667         MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
01668         /* can't be set to false */ true, /* interface */ true )->getText(),
01669         $args );
01670 }
01671 
01695 function wfMsgExt( $key, $options ) {
01696     wfDeprecated( __METHOD__, '1.21' );
01697 
01698     $args = func_get_args();
01699     array_shift( $args );
01700     array_shift( $args );
01701     $options = (array)$options;
01702 
01703     foreach ( $options as $arrayKey => $option ) {
01704         if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
01705             # An unknown index, neither numeric nor "language"
01706             wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
01707         } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
01708         array( 'parse', 'parseinline', 'escape', 'escapenoentities',
01709         'replaceafter', 'parsemag', 'content' ) ) ) {
01710             # A numeric index with unknown value
01711             wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
01712         }
01713     }
01714 
01715     if ( in_array( 'content', $options, true ) ) {
01716         $forContent = true;
01717         $langCode = true;
01718         $langCodeObj = null;
01719     } elseif ( array_key_exists( 'language', $options ) ) {
01720         $forContent = false;
01721         $langCode = wfGetLangObj( $options['language'] );
01722         $langCodeObj = $langCode;
01723     } else {
01724         $forContent = false;
01725         $langCode = false;
01726         $langCodeObj = null;
01727     }
01728 
01729     $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
01730 
01731     if ( !in_array( 'replaceafter', $options, true ) ) {
01732         $string = wfMsgReplaceArgs( $string, $args );
01733     }
01734 
01735     $messageCache = MessageCache::singleton();
01736     $parseInline = in_array( 'parseinline', $options, true );
01737     if ( in_array( 'parse', $options, true ) || $parseInline ) {
01738         $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
01739         if ( $string instanceof ParserOutput ) {
01740             $string = $string->getText();
01741         }
01742 
01743         if ( $parseInline ) {
01744             $string = Parser::stripOuterParagraph( $string );
01745         }
01746     } elseif ( in_array( 'parsemag', $options, true ) ) {
01747         $string = $messageCache->transform( $string,
01748                 !$forContent, $langCodeObj );
01749     }
01750 
01751     if ( in_array( 'escape', $options, true ) ) {
01752         $string = htmlspecialchars ( $string );
01753     } elseif ( in_array( 'escapenoentities', $options, true ) ) {
01754         $string = Sanitizer::escapeHtmlAllowEntities( $string );
01755     }
01756 
01757     if ( in_array( 'replaceafter', $options, true ) ) {
01758         $string = wfMsgReplaceArgs( $string, $args );
01759     }
01760 
01761     return $string;
01762 }
01763 
01774 function wfEmptyMsg( $key ) {
01775     wfDeprecated( __METHOD__, '1.21' );
01776 
01777     return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false;
01778 }
01779 
01787 function wfHostname() {
01788     static $host;
01789     if ( is_null( $host ) ) {
01790 
01791         # Hostname overriding
01792         global $wgOverrideHostname;
01793         if ( $wgOverrideHostname !== false ) {
01794             # Set static and skip any detection
01795             $host = $wgOverrideHostname;
01796             return $host;
01797         }
01798 
01799         if ( function_exists( 'posix_uname' ) ) {
01800             // This function not present on Windows
01801             $uname = posix_uname();
01802         } else {
01803             $uname = false;
01804         }
01805         if ( is_array( $uname ) && isset( $uname['nodename'] ) ) {
01806             $host = $uname['nodename'];
01807         } elseif ( getenv( 'COMPUTERNAME' ) ) {
01808             # Windows computer name
01809             $host = getenv( 'COMPUTERNAME' );
01810         } else {
01811             # This may be a virtual server.
01812             $host = $_SERVER['SERVER_NAME'];
01813         }
01814     }
01815     return $host;
01816 }
01817 
01827 function wfReportTime() {
01828     global $wgRequestTime, $wgShowHostnames;
01829 
01830     $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 );
01831     $reportVars = array( 'wgBackendResponseTime' => $responseTime );
01832     if ( $wgShowHostnames ) {
01833         $reportVars['wgHostname'] = wfHostname();
01834     }
01835     return Skin::makeVariablesScript( $reportVars );
01836 }
01837 
01848 function wfDebugBacktrace( $limit = 0 ) {
01849     static $disabled = null;
01850 
01851     if ( is_null( $disabled ) ) {
01852         $disabled = !function_exists( 'debug_backtrace' );
01853         if ( $disabled ) {
01854             wfDebug( "debug_backtrace() is disabled\n" );
01855         }
01856     }
01857     if ( $disabled ) {
01858         return array();
01859     }
01860 
01861     if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) {
01862         return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 );
01863     } else {
01864         return array_slice( debug_backtrace(), 1 );
01865     }
01866 }
01867 
01873 function wfBacktrace() {
01874     global $wgCommandLineMode;
01875 
01876     if ( $wgCommandLineMode ) {
01877         $msg = '';
01878     } else {
01879         $msg = "<ul>\n";
01880     }
01881     $backtrace = wfDebugBacktrace();
01882     foreach ( $backtrace as $call ) {
01883         if ( isset( $call['file'] ) ) {
01884             $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
01885             $file = $f[count( $f ) - 1];
01886         } else {
01887             $file = '-';
01888         }
01889         if ( isset( $call['line'] ) ) {
01890             $line = $call['line'];
01891         } else {
01892             $line = '-';
01893         }
01894         if ( $wgCommandLineMode ) {
01895             $msg .= "$file line $line calls ";
01896         } else {
01897             $msg .= '<li>' . $file . ' line ' . $line . ' calls ';
01898         }
01899         if ( !empty( $call['class'] ) ) {
01900             $msg .= $call['class'] . $call['type'];
01901         }
01902         $msg .= $call['function'] . '()';
01903 
01904         if ( $wgCommandLineMode ) {
01905             $msg .= "\n";
01906         } else {
01907             $msg .= "</li>\n";
01908         }
01909     }
01910     if ( $wgCommandLineMode ) {
01911         $msg .= "\n";
01912     } else {
01913         $msg .= "</ul>\n";
01914     }
01915 
01916     return $msg;
01917 }
01918 
01928 function wfGetCaller( $level = 2 ) {
01929     $backtrace = wfDebugBacktrace( $level + 1 );
01930     if ( isset( $backtrace[$level] ) ) {
01931         return wfFormatStackFrame( $backtrace[$level] );
01932     } else {
01933         return 'unknown';
01934     }
01935 }
01936 
01944 function wfGetAllCallers( $limit = 3 ) {
01945     $trace = array_reverse( wfDebugBacktrace() );
01946     if ( !$limit || $limit > count( $trace ) - 1 ) {
01947         $limit = count( $trace ) - 1;
01948     }
01949     $trace = array_slice( $trace, -$limit - 1, $limit );
01950     return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
01951 }
01952 
01959 function wfFormatStackFrame( $frame ) {
01960     return isset( $frame['class'] ) ?
01961         $frame['class'] . '::' . $frame['function'] :
01962         $frame['function'];
01963 }
01964 
01965 /* Some generic result counters, pulled out of SearchEngine */
01966 
01974 function wfShowingResults( $offset, $limit ) {
01975     return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse();
01976 }
01977 
01985 function wfClientAcceptsGzip( $force = false ) {
01986     static $result = null;
01987     if ( $result === null || $force ) {
01988         $result = false;
01989         if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
01990             # @todo FIXME: We may want to blacklist some broken browsers
01991             $m = array();
01992             if ( preg_match(
01993                     '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
01994                     $_SERVER['HTTP_ACCEPT_ENCODING'],
01995                     $m
01996                 )
01997             ) {
01998                 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
01999                     $result = false;
02000                     return $result;
02001                 }
02002                 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
02003                 $result = true;
02004             }
02005         }
02006     }
02007     return $result;
02008 }
02009 
02019 function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
02020     global $wgRequest;
02021     wfDeprecated( __METHOD__, '1.24' );
02022     return $wgRequest->getLimitOffset( $deflimit, $optionname );
02023 }
02024 
02034 function wfEscapeWikiText( $text ) {
02035     static $repl = null, $repl2 = null;
02036     if ( $repl === null ) {
02037         $repl = array(
02038             '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
02039             '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
02040             '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
02041             "\n#" => "\n&#35;", "\r#" => "\r&#35;",
02042             "\n*" => "\n&#42;", "\r*" => "\r&#42;",
02043             "\n:" => "\n&#58;", "\r:" => "\r&#58;",
02044             "\n " => "\n&#32;", "\r " => "\r&#32;",
02045             "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
02046             "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
02047             "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
02048             "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
02049             '__' => '_&#95;', '://' => '&#58;//',
02050         );
02051 
02052         // We have to catch everything "\s" matches in PCRE
02053         foreach ( array( 'ISBN', 'RFC', 'PMID' ) as $magic ) {
02054             $repl["$magic "] = "$magic&#32;";
02055             $repl["$magic\t"] = "$magic&#9;";
02056             $repl["$magic\r"] = "$magic&#13;";
02057             $repl["$magic\n"] = "$magic&#10;";
02058             $repl["$magic\f"] = "$magic&#12;";
02059         }
02060 
02061         // And handle protocols that don't use "://"
02062         global $wgUrlProtocols;
02063         $repl2 = array();
02064         foreach ( $wgUrlProtocols as $prot ) {
02065             if ( substr( $prot, -1 ) === ':' ) {
02066                 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' );
02067             }
02068         }
02069         $repl2 = $repl2 ? '/\b(' . join( '|', $repl2 ) . '):/i' : '/^(?!)/';
02070     }
02071     $text = substr( strtr( "\n$text", $repl ), 1 );
02072     $text = preg_replace( $repl2, '$1&#58;', $text );
02073     return $text;
02074 }
02075 
02086 function wfSetVar( &$dest, $source, $force = false ) {
02087     $temp = $dest;
02088     if ( !is_null( $source ) || $force ) {
02089         $dest = $source;
02090     }
02091     return $temp;
02092 }
02093 
02103 function wfSetBit( &$dest, $bit, $state = true ) {
02104     $temp = (bool)( $dest & $bit );
02105     if ( !is_null( $state ) ) {
02106         if ( $state ) {
02107             $dest |= $bit;
02108         } else {
02109             $dest &= ~$bit;
02110         }
02111     }
02112     return $temp;
02113 }
02114 
02121 function wfVarDump( $var ) {
02122     global $wgOut;
02123     $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
02124     if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
02125         print $s;
02126     } else {
02127         $wgOut->addHTML( $s );
02128     }
02129 }
02130 
02138 function wfHttpError( $code, $label, $desc ) {
02139     global $wgOut;
02140     $wgOut->disable();
02141     header( "HTTP/1.0 $code $label" );
02142     header( "Status: $code $label" );
02143     $wgOut->sendCacheControl();
02144 
02145     header( 'Content-type: text/html; charset=utf-8' );
02146     print "<!doctype html>" .
02147         '<html><head><title>' .
02148         htmlspecialchars( $label ) .
02149         '</title></head><body><h1>' .
02150         htmlspecialchars( $label ) .
02151         '</h1><p>' .
02152         nl2br( htmlspecialchars( $desc ) ) .
02153         "</p></body></html>\n";
02154 }
02155 
02173 function wfResetOutputBuffers( $resetGzipEncoding = true ) {
02174     if ( $resetGzipEncoding ) {
02175         // Suppress Content-Encoding and Content-Length
02176         // headers from 1.10+s wfOutputHandler
02177         global $wgDisableOutputCompression;
02178         $wgDisableOutputCompression = true;
02179     }
02180     while ( $status = ob_get_status() ) {
02181         if ( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
02182             // Probably from zlib.output_compression or other
02183             // PHP-internal setting which can't be removed.
02184             //
02185             // Give up, and hope the result doesn't break
02186             // output behavior.
02187             break;
02188         }
02189         if ( !ob_end_clean() ) {
02190             // Could not remove output buffer handler; abort now
02191             // to avoid getting in some kind of infinite loop.
02192             break;
02193         }
02194         if ( $resetGzipEncoding ) {
02195             if ( $status['name'] == 'ob_gzhandler' ) {
02196                 // Reset the 'Content-Encoding' field set by this handler
02197                 // so we can start fresh.
02198                 header_remove( 'Content-Encoding' );
02199                 break;
02200             }
02201         }
02202     }
02203 }
02204 
02217 function wfClearOutputBuffers() {
02218     wfResetOutputBuffers( false );
02219 }
02220 
02229 function wfAcceptToPrefs( $accept, $def = '*/*' ) {
02230     # No arg means accept anything (per HTTP spec)
02231     if ( !$accept ) {
02232         return array( $def => 1.0 );
02233     }
02234 
02235     $prefs = array();
02236 
02237     $parts = explode( ',', $accept );
02238 
02239     foreach ( $parts as $part ) {
02240         # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
02241         $values = explode( ';', trim( $part ) );
02242         $match = array();
02243         if ( count( $values ) == 1 ) {
02244             $prefs[$values[0]] = 1.0;
02245         } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
02246             $prefs[$values[0]] = floatval( $match[1] );
02247         }
02248     }
02249 
02250     return $prefs;
02251 }
02252 
02265 function mimeTypeMatch( $type, $avail ) {
02266     if ( array_key_exists( $type, $avail ) ) {
02267         return $type;
02268     } else {
02269         $parts = explode( '/', $type );
02270         if ( array_key_exists( $parts[0] . '/*', $avail ) ) {
02271             return $parts[0] . '/*';
02272         } elseif ( array_key_exists( '*/*', $avail ) ) {
02273             return '*/*';
02274         } else {
02275             return null;
02276         }
02277     }
02278 }
02279 
02293 function wfNegotiateType( $cprefs, $sprefs ) {
02294     $combine = array();
02295 
02296     foreach ( array_keys( $sprefs ) as $type ) {
02297         $parts = explode( '/', $type );
02298         if ( $parts[1] != '*' ) {
02299             $ckey = mimeTypeMatch( $type, $cprefs );
02300             if ( $ckey ) {
02301                 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
02302             }
02303         }
02304     }
02305 
02306     foreach ( array_keys( $cprefs ) as $type ) {
02307         $parts = explode( '/', $type );
02308         if ( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
02309             $skey = mimeTypeMatch( $type, $sprefs );
02310             if ( $skey ) {
02311                 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
02312             }
02313         }
02314     }
02315 
02316     $bestq = 0;
02317     $besttype = null;
02318 
02319     foreach ( array_keys( $combine ) as $type ) {
02320         if ( $combine[$type] > $bestq ) {
02321             $besttype = $type;
02322             $bestq = $combine[$type];
02323         }
02324     }
02325 
02326     return $besttype;
02327 }
02328 
02334 function wfSuppressWarnings( $end = false ) {
02335     static $suppressCount = 0;
02336     static $originalLevel = false;
02337 
02338     if ( $end ) {
02339         if ( $suppressCount ) {
02340             --$suppressCount;
02341             if ( !$suppressCount ) {
02342                 error_reporting( $originalLevel );
02343             }
02344         }
02345     } else {
02346         if ( !$suppressCount ) {
02347             $originalLevel = error_reporting( E_ALL & ~(
02348                 E_WARNING |
02349                 E_NOTICE |
02350                 E_USER_WARNING |
02351                 E_USER_NOTICE |
02352                 E_DEPRECATED |
02353                 E_USER_DEPRECATED |
02354                 E_STRICT
02355             ) );
02356         }
02357         ++$suppressCount;
02358     }
02359 }
02360 
02364 function wfRestoreWarnings() {
02365     wfSuppressWarnings( true );
02366 }
02367 
02368 # Autodetect, convert and provide timestamps of various types
02369 
02373 define( 'TS_UNIX', 0 );
02374 
02378 define( 'TS_MW', 1 );
02379 
02383 define( 'TS_DB', 2 );
02384 
02388 define( 'TS_RFC2822', 3 );
02389 
02395 define( 'TS_ISO_8601', 4 );
02396 
02404 define( 'TS_EXIF', 5 );
02405 
02409 define( 'TS_ORACLE', 6 );
02410 
02414 define( 'TS_POSTGRES', 7 );
02415 
02419 define( 'TS_ISO_8601_BASIC', 9 );
02420 
02429 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
02430     try {
02431         $timestamp = new MWTimestamp( $ts );
02432         return $timestamp->getTimestamp( $outputtype );
02433     } catch ( TimestampException $e ) {
02434         wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" );
02435         return false;
02436     }
02437 }
02438 
02447 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
02448     if ( is_null( $ts ) ) {
02449         return null;
02450     } else {
02451         return wfTimestamp( $outputtype, $ts );
02452     }
02453 }
02454 
02460 function wfTimestampNow() {
02461     # return NOW
02462     return wfTimestamp( TS_MW, time() );
02463 }
02464 
02470 function wfIsWindows() {
02471     static $isWindows = null;
02472     if ( $isWindows === null ) {
02473         $isWindows = substr( php_uname(), 0, 7 ) == 'Windows';
02474     }
02475     return $isWindows;
02476 }
02477 
02483 function wfIsHHVM() {
02484     return defined( 'HHVM_VERSION' );
02485 }
02486 
02494 function swap( &$x, &$y ) {
02495     wfDeprecated( __FUNCTION__, '1.24' );
02496     $z = $x;
02497     $x = $y;
02498     $y = $z;
02499 }
02500 
02512 function wfTempDir() {
02513     global $wgTmpDirectory;
02514 
02515     if ( $wgTmpDirectory !== false ) {
02516         return $wgTmpDirectory;
02517     }
02518 
02519     $tmpDir = array_map( "getenv", array( 'TMPDIR', 'TMP', 'TEMP' ) );
02520 
02521     foreach ( $tmpDir as $tmp ) {
02522         if ( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
02523             return $tmp;
02524         }
02525     }
02526     return sys_get_temp_dir();
02527 }
02528 
02538 function wfMkdirParents( $dir, $mode = null, $caller = null ) {
02539     global $wgDirectoryMode;
02540 
02541     if ( FileBackend::isStoragePath( $dir ) ) { // sanity
02542         throw new MWException( __FUNCTION__ . " given storage path '$dir'." );
02543     }
02544 
02545     if ( !is_null( $caller ) ) {
02546         wfDebug( "$caller: called wfMkdirParents($dir)\n" );
02547     }
02548 
02549     if ( strval( $dir ) === '' || ( file_exists( $dir ) && is_dir( $dir ) ) ) {
02550         return true;
02551     }
02552 
02553     $dir = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $dir );
02554 
02555     if ( is_null( $mode ) ) {
02556         $mode = $wgDirectoryMode;
02557     }
02558 
02559     // Turn off the normal warning, we're doing our own below
02560     wfSuppressWarnings();
02561     $ok = mkdir( $dir, $mode, true ); // PHP5 <3
02562     wfRestoreWarnings();
02563 
02564     if ( !$ok ) {
02565         //directory may have been created on another request since we last checked
02566         if ( is_dir( $dir ) ) {
02567             return true;
02568         }
02569 
02570         // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
02571         wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) );
02572     }
02573     return $ok;
02574 }
02575 
02581 function wfRecursiveRemoveDir( $dir ) {
02582     wfDebug( __FUNCTION__ . "( $dir )\n" );
02583     // taken from http://de3.php.net/manual/en/function.rmdir.php#98622
02584     if ( is_dir( $dir ) ) {
02585         $objects = scandir( $dir );
02586         foreach ( $objects as $object ) {
02587             if ( $object != "." && $object != ".." ) {
02588                 if ( filetype( $dir . '/' . $object ) == "dir" ) {
02589                     wfRecursiveRemoveDir( $dir . '/' . $object );
02590                 } else {
02591                     unlink( $dir . '/' . $object );
02592                 }
02593             }
02594         }
02595         reset( $objects );
02596         rmdir( $dir );
02597     }
02598 }
02599 
02606 function wfPercent( $nr, $acc = 2, $round = true ) {
02607     $ret = sprintf( "%.${acc}f", $nr );
02608     return $round ? round( $ret, $acc ) . '%' : "$ret%";
02609 }
02610 
02634 function wfIniGetBool( $setting ) {
02635     $val = strtolower( ini_get( $setting ) );
02636     // 'on' and 'true' can't have whitespace around them, but '1' can.
02637     return $val == 'on'
02638         || $val == 'true'
02639         || $val == 'yes'
02640         || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
02641 }
02642 
02654 function wfEscapeShellArg( /*...*/ ) {
02655     wfInitShellLocale();
02656 
02657     $args = func_get_args();
02658     $first = true;
02659     $retVal = '';
02660     foreach ( $args as $arg ) {
02661         if ( !$first ) {
02662             $retVal .= ' ';
02663         } else {
02664             $first = false;
02665         }
02666 
02667         if ( wfIsWindows() ) {
02668             // Escaping for an MSVC-style command line parser and CMD.EXE
02669             // @codingStandardsIgnoreStart For long URLs
02670             // Refs:
02671             //  * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
02672             //  * http://technet.microsoft.com/en-us/library/cc723564.aspx
02673             //  * Bug #13518
02674             //  * CR r63214
02675             // Double the backslashes before any double quotes. Escape the double quotes.
02676             // @codingStandardsIgnoreEnd
02677             $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
02678             $arg = '';
02679             $iteration = 0;
02680             foreach ( $tokens as $token ) {
02681                 if ( $iteration % 2 == 1 ) {
02682                     // Delimiter, a double quote preceded by zero or more slashes
02683                     $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
02684                 } elseif ( $iteration % 4 == 2 ) {
02685                     // ^ in $token will be outside quotes, need to be escaped
02686                     $arg .= str_replace( '^', '^^', $token );
02687                 } else { // $iteration % 4 == 0
02688                     // ^ in $token will appear inside double quotes, so leave as is
02689                     $arg .= $token;
02690                 }
02691                 $iteration++;
02692             }
02693             // Double the backslashes before the end of the string, because
02694             // we will soon add a quote
02695             $m = array();
02696             if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
02697                 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
02698             }
02699 
02700             // Add surrounding quotes
02701             $retVal .= '"' . $arg . '"';
02702         } else {
02703             $retVal .= escapeshellarg( $arg );
02704         }
02705     }
02706     return $retVal;
02707 }
02708 
02715 function wfShellExecDisabled() {
02716     static $disabled = null;
02717     if ( is_null( $disabled ) ) {
02718         if ( wfIniGetBool( 'safe_mode' ) ) {
02719             wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
02720             $disabled = 'safemode';
02721         } elseif ( !function_exists( 'proc_open' ) ) {
02722             wfDebug( "proc_open() is disabled\n" );
02723             $disabled = 'disabled';
02724         } else {
02725             $disabled = false;
02726         }
02727     }
02728     return $disabled;
02729 }
02730 
02751 function wfShellExec( $cmd, &$retval = null, $environ = array(),
02752     $limits = array(), $options = array()
02753 ) {
02754     global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime,
02755         $wgMaxShellWallClockTime, $wgShellCgroup;
02756 
02757     $disabled = wfShellExecDisabled();
02758     if ( $disabled ) {
02759         $retval = 1;
02760         return $disabled == 'safemode' ?
02761             'Unable to run external programs in safe mode.' :
02762             'Unable to run external programs, proc_open() is disabled.';
02763     }
02764 
02765     $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr'];
02766 
02767     wfInitShellLocale();
02768 
02769     $envcmd = '';
02770     foreach ( $environ as $k => $v ) {
02771         if ( wfIsWindows() ) {
02772             /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
02773              * appear in the environment variable, so we must use carat escaping as documented in
02774              * http://technet.microsoft.com/en-us/library/cc723564.aspx
02775              * Note however that the quote isn't listed there, but is needed, and the parentheses
02776              * are listed there but doesn't appear to need it.
02777              */
02778             $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& ';
02779         } else {
02780             /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
02781              * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
02782              */
02783             $envcmd .= "$k=" . escapeshellarg( $v ) . ' ';
02784         }
02785     }
02786     if ( is_array( $cmd ) ) {
02787         // Command line may be given as an array, escape each value and glue them together with a space
02788         $cmdVals = array();
02789         foreach ( $cmd as $val ) {
02790             $cmdVals[] = wfEscapeShellArg( $val );
02791         }
02792         $cmd = implode( ' ', $cmdVals );
02793     }
02794 
02795     $cmd = $envcmd . $cmd;
02796 
02797     $useLogPipe = false;
02798     if ( is_executable( '/bin/bash' ) ) {
02799         $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime );
02800         if ( isset( $limits['walltime'] ) ) {
02801             $wallTime = intval( $limits['walltime'] );
02802         } elseif ( isset( $limits['time'] ) ) {
02803             $wallTime = $time;
02804         } else {
02805             $wallTime = intval( $wgMaxShellWallClockTime );
02806         }
02807         $mem = intval ( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory );
02808         $filesize = intval ( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize );
02809 
02810         if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) {
02811             $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' .
02812                 escapeshellarg( $cmd ) . ' ' .
02813                 escapeshellarg(
02814                     "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' .
02815                     "MW_CPU_LIMIT=$time; " .
02816                     'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' .
02817                     "MW_MEM_LIMIT=$mem; " .
02818                     "MW_FILE_SIZE_LIMIT=$filesize; " .
02819                     "MW_WALL_CLOCK_LIMIT=$wallTime; " .
02820                     "MW_USE_LOG_PIPE=yes"
02821                 );
02822             $useLogPipe = true;
02823         } elseif ( $includeStderr ) {
02824             $cmd .= ' 2>&1';
02825         }
02826     } elseif ( $includeStderr ) {
02827         $cmd .= ' 2>&1';
02828     }
02829     wfDebug( "wfShellExec: $cmd\n" );
02830 
02831     $desc = array(
02832         0 => array( 'file', 'php://stdin', 'r' ),
02833         1 => array( 'pipe', 'w' ),
02834         2 => array( 'file', 'php://stderr', 'w' ) );
02835     if ( $useLogPipe ) {
02836         $desc[3] = array( 'pipe', 'w' );
02837     }
02838     $pipes = null;
02839     $proc = proc_open( $cmd, $desc, $pipes );
02840     if ( !$proc ) {
02841         wfDebugLog( 'exec', "proc_open() failed: $cmd" );
02842         $retval = -1;
02843         return '';
02844     }
02845     $outBuffer = $logBuffer = '';
02846     $emptyArray = array();
02847     $status = false;
02848     $logMsg = false;
02849 
02850     // According to the documentation, it is possible for stream_select()
02851     // to fail due to EINTR. I haven't managed to induce this in testing
02852     // despite sending various signals. If it did happen, the error
02853     // message would take the form:
02854     //
02855     // stream_select(): unable to select [4]: Interrupted system call (max_fd=5)
02856     //
02857     // where [4] is the value of the macro EINTR and "Interrupted system
02858     // call" is string which according to the Linux manual is "possibly"
02859     // localised according to LC_MESSAGES.
02860     $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4;
02861     $eintrMessage = "stream_select(): unable to select [$eintr]";
02862 
02863     // Build a table mapping resource IDs to pipe FDs to work around a
02864     // PHP 5.3 issue in which stream_select() does not preserve array keys
02865     // <https://bugs.php.net/bug.php?id=53427>.
02866     $fds = array();
02867     foreach ( $pipes as $fd => $pipe ) {
02868         $fds[(int)$pipe] = $fd;
02869     }
02870 
02871     $running = true;
02872     $timeout = null;
02873     $numReadyPipes = 0;
02874 
02875     while ( $running === true || $numReadyPipes !== 0 ) {
02876         if ( $running ) {
02877             $status = proc_get_status( $proc );
02878             // If the process has terminated, switch to nonblocking selects
02879             // for getting any data still waiting to be read.
02880             if ( !$status['running'] ) {
02881                 $running = false;
02882                 $timeout = 0;
02883             }
02884         }
02885 
02886         $readyPipes = $pipes;
02887 
02888         // Clear last error
02889         // @codingStandardsIgnoreStart Generic.PHP.NoSilencedErrors.Discouraged
02890         @trigger_error( '' );
02891         $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout );
02892         if ( $numReadyPipes === false ) {
02893             // @codingStandardsIgnoreEnd
02894             $error = error_get_last();
02895             if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) {
02896                 continue;
02897             } else {
02898                 trigger_error( $error['message'], E_USER_WARNING );
02899                 $logMsg = $error['message'];
02900                 break;
02901             }
02902         }
02903         foreach ( $readyPipes as $pipe ) {
02904             $block = fread( $pipe, 65536 );
02905             $fd = $fds[(int)$pipe];
02906             if ( $block === '' ) {
02907                 // End of file
02908                 fclose( $pipes[$fd] );
02909                 unset( $pipes[$fd] );
02910                 if ( !$pipes ) {
02911                     break 2;
02912                 }
02913             } elseif ( $block === false ) {
02914                 // Read error
02915                 $logMsg = "Error reading from pipe";
02916                 break 2;
02917             } elseif ( $fd == 1 ) {
02918                 // From stdout
02919                 $outBuffer .= $block;
02920             } elseif ( $fd == 3 ) {
02921                 // From log FD
02922                 $logBuffer .= $block;
02923                 if ( strpos( $block, "\n" ) !== false ) {
02924                     $lines = explode( "\n", $logBuffer );
02925                     $logBuffer = array_pop( $lines );
02926                     foreach ( $lines as $line ) {
02927                         wfDebugLog( 'exec', $line );
02928                     }
02929                 }
02930             }
02931         }
02932     }
02933 
02934     foreach ( $pipes as $pipe ) {
02935         fclose( $pipe );
02936     }
02937 
02938     // Use the status previously collected if possible, since proc_get_status()
02939     // just calls waitpid() which will not return anything useful the second time.
02940     if ( $running ) {
02941         $status = proc_get_status( $proc );
02942     }
02943 
02944     if ( $logMsg !== false ) {
02945         // Read/select error
02946         $retval = -1;
02947         proc_close( $proc );
02948     } elseif ( $status['signaled'] ) {
02949         $logMsg = "Exited with signal {$status['termsig']}";
02950         $retval = 128 + $status['termsig'];
02951         proc_close( $proc );
02952     } else {
02953         if ( $status['running'] ) {
02954             $retval = proc_close( $proc );
02955         } else {
02956             $retval = $status['exitcode'];
02957             proc_close( $proc );
02958         }
02959         if ( $retval == 127 ) {
02960             $logMsg = "Possibly missing executable file";
02961         } elseif ( $retval >= 129 && $retval <= 192 ) {
02962             $logMsg = "Probably exited with signal " . ( $retval - 128 );
02963         }
02964     }
02965 
02966     if ( $logMsg !== false ) {
02967         wfDebugLog( 'exec', "$logMsg: $cmd" );
02968     }
02969 
02970     return $outBuffer;
02971 }
02972 
02987 function wfShellExecWithStderr( $cmd, &$retval = null, $environ = array(), $limits = array() ) {
02988     return wfShellExec( $cmd, $retval, $environ, $limits, array( 'duplicateStderr' => true ) );
02989 }
02990 
02995 function wfInitShellLocale() {
02996     static $done = false;
02997     if ( $done ) {
02998         return;
02999     }
03000     $done = true;
03001     global $wgShellLocale;
03002     if ( !wfIniGetBool( 'safe_mode' ) ) {
03003         putenv( "LC_CTYPE=$wgShellLocale" );
03004         setlocale( LC_CTYPE, $wgShellLocale );
03005     }
03006 }
03007 
03013 function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) {
03014     return wfShellWikiCmd( $script, $parameters, $options );
03015 }
03016 
03029 function wfShellWikiCmd( $script, array $parameters = array(), array $options = array() ) {
03030     global $wgPhpCli;
03031     // Give site config file a chance to run the script in a wrapper.
03032     // The caller may likely want to call wfBasename() on $script.
03033     wfRunHooks( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) );
03034     $cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli );
03035     if ( isset( $options['wrapper'] ) ) {
03036         $cmd[] = $options['wrapper'];
03037     }
03038     $cmd[] = $script;
03039     // Escape each parameter for shell
03040     return implode( " ", array_map( 'wfEscapeShellArg', array_merge( $cmd, $parameters ) ) );
03041 }
03042 
03053 function wfMerge( $old, $mine, $yours, &$result ) {
03054     global $wgDiff3;
03055 
03056     # This check may also protect against code injection in
03057     # case of broken installations.
03058     wfSuppressWarnings();
03059     $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
03060     wfRestoreWarnings();
03061 
03062     if ( !$haveDiff3 ) {
03063         wfDebug( "diff3 not found\n" );
03064         return false;
03065     }
03066 
03067     # Make temporary files
03068     $td = wfTempDir();
03069     $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
03070     $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
03071     $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
03072 
03073     # NOTE: diff3 issues a warning to stderr if any of the files does not end with
03074     #       a newline character. To avoid this, we normalize the trailing whitespace before
03075     #       creating the diff.
03076 
03077     fwrite( $oldtextFile, rtrim( $old ) . "\n" );
03078     fclose( $oldtextFile );
03079     fwrite( $mytextFile, rtrim( $mine ) . "\n" );
03080     fclose( $mytextFile );
03081     fwrite( $yourtextFile, rtrim( $yours ) . "\n" );
03082     fclose( $yourtextFile );
03083 
03084     # Check for a conflict
03085     $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' .
03086         wfEscapeShellArg( $mytextName ) . ' ' .
03087         wfEscapeShellArg( $oldtextName ) . ' ' .
03088         wfEscapeShellArg( $yourtextName );
03089     $handle = popen( $cmd, 'r' );
03090 
03091     if ( fgets( $handle, 1024 ) ) {
03092         $conflict = true;
03093     } else {
03094         $conflict = false;
03095     }
03096     pclose( $handle );
03097 
03098     # Merge differences
03099     $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' .
03100         wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
03101     $handle = popen( $cmd, 'r' );
03102     $result = '';
03103     do {
03104         $data = fread( $handle, 8192 );
03105         if ( strlen( $data ) == 0 ) {
03106             break;
03107         }
03108         $result .= $data;
03109     } while ( true );
03110     pclose( $handle );
03111     unlink( $mytextName );
03112     unlink( $oldtextName );
03113     unlink( $yourtextName );
03114 
03115     if ( $result === '' && $old !== '' && !$conflict ) {
03116         wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
03117         $conflict = true;
03118     }
03119     return !$conflict;
03120 }
03121 
03131 function wfDiff( $before, $after, $params = '-u' ) {
03132     if ( $before == $after ) {
03133         return '';
03134     }
03135 
03136     global $wgDiff;
03137     wfSuppressWarnings();
03138     $haveDiff = $wgDiff && file_exists( $wgDiff );
03139     wfRestoreWarnings();
03140 
03141     # This check may also protect against code injection in
03142     # case of broken installations.
03143     if ( !$haveDiff ) {
03144         wfDebug( "diff executable not found\n" );
03145         $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
03146         $format = new UnifiedDiffFormatter();
03147         return $format->format( $diffs );
03148     }
03149 
03150     # Make temporary files
03151     $td = wfTempDir();
03152     $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
03153     $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
03154 
03155     fwrite( $oldtextFile, $before );
03156     fclose( $oldtextFile );
03157     fwrite( $newtextFile, $after );
03158     fclose( $newtextFile );
03159 
03160     // Get the diff of the two files
03161     $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
03162 
03163     $h = popen( $cmd, 'r' );
03164 
03165     $diff = '';
03166 
03167     do {
03168         $data = fread( $h, 8192 );
03169         if ( strlen( $data ) == 0 ) {
03170             break;
03171         }
03172         $diff .= $data;
03173     } while ( true );
03174 
03175     // Clean up
03176     pclose( $h );
03177     unlink( $oldtextName );
03178     unlink( $newtextName );
03179 
03180     // Kill the --- and +++ lines. They're not useful.
03181     $diff_lines = explode( "\n", $diff );
03182     if ( isset( $diff_lines[0] ) && strpos( $diff_lines[0], '---' ) === 0 ) {
03183         unset( $diff_lines[0] );
03184     }
03185     if ( isset( $diff_lines[1] ) && strpos( $diff_lines[1], '+++' ) === 0 ) {
03186         unset( $diff_lines[1] );
03187     }
03188 
03189     $diff = implode( "\n", $diff_lines );
03190 
03191     return $diff;
03192 }
03193 
03209 function wfUsePHP( $req_ver ) {
03210     $php_ver = PHP_VERSION;
03211 
03212     if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
03213         throw new MWException( "PHP $req_ver required--this is only $php_ver" );
03214     }
03215 }
03216 
03238 function wfUseMW( $req_ver ) {
03239     global $wgVersion;
03240 
03241     if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
03242         throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
03243     }
03244 }
03245 
03258 function wfBaseName( $path, $suffix = '' ) {
03259     if ( $suffix == '' ) {
03260         $encSuffix = '';
03261     } else {
03262         $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?';
03263     }
03264 
03265     $matches = array();
03266     if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
03267         return $matches[1];
03268     } else {
03269         return '';
03270     }
03271 }
03272 
03282 function wfRelativePath( $path, $from ) {
03283     // Normalize mixed input on Windows...
03284     $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
03285     $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
03286 
03287     // Trim trailing slashes -- fix for drive root
03288     $path = rtrim( $path, DIRECTORY_SEPARATOR );
03289     $from = rtrim( $from, DIRECTORY_SEPARATOR );
03290 
03291     $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
03292     $against = explode( DIRECTORY_SEPARATOR, $from );
03293 
03294     if ( $pieces[0] !== $against[0] ) {
03295         // Non-matching Windows drive letters?
03296         // Return a full path.
03297         return $path;
03298     }
03299 
03300     // Trim off common prefix
03301     while ( count( $pieces ) && count( $against )
03302         && $pieces[0] == $against[0] ) {
03303         array_shift( $pieces );
03304         array_shift( $against );
03305     }
03306 
03307     // relative dots to bump us to the parent
03308     while ( count( $against ) ) {
03309         array_unshift( $pieces, '..' );
03310         array_shift( $against );
03311     }
03312 
03313     array_push( $pieces, wfBaseName( $path ) );
03314 
03315     return implode( DIRECTORY_SEPARATOR, $pieces );
03316 }
03317 
03333 function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
03334     $lowercase = true, $engine = 'auto'
03335 ) {
03336     $input = (string)$input;
03337     if (
03338         $sourceBase < 2 ||
03339         $sourceBase > 36 ||
03340         $destBase < 2 ||
03341         $destBase > 36 ||
03342         $sourceBase != (int)$sourceBase ||
03343         $destBase != (int)$destBase ||
03344         $pad != (int)$pad ||
03345         !preg_match(
03346             "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i",
03347             $input
03348         )
03349     ) {
03350         return false;
03351     }
03352 
03353     static $baseChars = array(
03354         10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f',
03355         16 => 'g', 17 => 'h', 18 => 'i', 19 => 'j', 20 => 'k', 21 => 'l',
03356         22 => 'm', 23 => 'n', 24 => 'o', 25 => 'p', 26 => 'q', 27 => 'r',
03357         28 => 's', 29 => 't', 30 => 'u', 31 => 'v', 32 => 'w', 33 => 'x',
03358         34 => 'y', 35 => 'z',
03359 
03360         '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5,
03361         '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'a' => 10, 'b' => 11,
03362         'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17,
03363         'i' => 18, 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23,
03364         'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, 's' => 28, 't' => 29,
03365         'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35
03366     );
03367 
03368     if ( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) {
03369         // Removing leading zeros works around broken base detection code in
03370         // some PHP versions (see <https://bugs.php.net/bug.php?id=50175> and
03371         // <https://bugs.php.net/bug.php?id=55398>).
03372         $result = gmp_strval( gmp_init( ltrim( $input, '0' ), $sourceBase ), $destBase );
03373     } elseif ( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) {
03374         $decimal = '0';
03375         foreach ( str_split( strtolower( $input ) ) as $char ) {
03376             $decimal = bcmul( $decimal, $sourceBase );
03377             $decimal = bcadd( $decimal, $baseChars[$char] );
03378         }
03379 
03380         // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
03381         for ( $result = ''; bccomp( $decimal, 0 ); $decimal = bcdiv( $decimal, $destBase, 0 ) ) {
03382             $result .= $baseChars[bcmod( $decimal, $destBase )];
03383         }
03384         // @codingStandardsIgnoreEnd
03385 
03386         $result = strrev( $result );
03387     } else {
03388         $inDigits = array();
03389         foreach ( str_split( strtolower( $input ) ) as $char ) {
03390             $inDigits[] = $baseChars[$char];
03391         }
03392 
03393         // Iterate over the input, modulo-ing out an output digit
03394         // at a time until input is gone.
03395         $result = '';
03396         while ( $inDigits ) {
03397             $work = 0;
03398             $workDigits = array();
03399 
03400             // Long division...
03401             foreach ( $inDigits as $digit ) {
03402                 $work *= $sourceBase;
03403                 $work += $digit;
03404 
03405                 if ( $workDigits || $work >= $destBase ) {
03406                     $workDigits[] = (int)( $work / $destBase );
03407                 }
03408                 $work %= $destBase;
03409             }
03410 
03411             // All that division leaves us with a remainder,
03412             // which is conveniently our next output digit.
03413             $result .= $baseChars[$work];
03414 
03415             // And we continue!
03416             $inDigits = $workDigits;
03417         }
03418 
03419         $result = strrev( $result );
03420     }
03421 
03422     if ( !$lowercase ) {
03423         $result = strtoupper( $result );
03424     }
03425 
03426     return str_pad( $result, $pad, '0', STR_PAD_LEFT );
03427 }
03428 
03434 function wfCheckEntropy() {
03435     return (
03436             ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
03437             || ini_get( 'session.entropy_file' )
03438         )
03439         && intval( ini_get( 'session.entropy_length' ) ) >= 32;
03440 }
03441 
03446 function wfFixSessionID() {
03447     // If the cookie or session id is already set we already have a session and should abort
03448     if ( isset( $_COOKIE[session_name()] ) || session_id() ) {
03449         return;
03450     }
03451 
03452     // PHP's built-in session entropy is enabled if:
03453     // - entropy_file is set or you're on Windows with php 5.3.3+
03454     // - AND entropy_length is > 0
03455     // We treat it as disabled if it doesn't have an entropy length of at least 32
03456     $entropyEnabled = wfCheckEntropy();
03457 
03458     // If built-in entropy is not enabled or not sufficient override PHP's
03459     // built in session id generation code
03460     if ( !$entropyEnabled ) {
03461         wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " .
03462             "overriding session id generation using our cryptrand source.\n" );
03463         session_id( MWCryptRand::generateHex( 32 ) );
03464     }
03465 }
03466 
03472 function wfResetSessionID() {
03473     global $wgCookieSecure;
03474     $oldSessionId = session_id();
03475     $cookieParams = session_get_cookie_params();
03476     if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) {
03477         session_regenerate_id( false );
03478     } else {
03479         $tmp = $_SESSION;
03480         session_destroy();
03481         wfSetupSession( MWCryptRand::generateHex( 32 ) );
03482         $_SESSION = $tmp;
03483     }
03484     $newSessionId = session_id();
03485     wfRunHooks( 'ResetSessionID', array( $oldSessionId, $newSessionId ) );
03486 }
03487 
03493 function wfSetupSession( $sessionId = false ) {
03494     global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain,
03495             $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler;
03496     if ( $wgSessionsInObjectCache || $wgSessionsInMemcached ) {
03497         ObjectCacheSessionHandler::install();
03498     } elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
03499         # Only set this if $wgSessionHandler isn't null and session.save_handler
03500         # hasn't already been set to the desired value (that causes errors)
03501         ini_set( 'session.save_handler', $wgSessionHandler );
03502     }
03503     session_set_cookie_params(
03504         0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly );
03505     session_cache_limiter( 'private, must-revalidate' );
03506     if ( $sessionId ) {
03507         session_id( $sessionId );
03508     } else {
03509         wfFixSessionID();
03510     }
03511     wfSuppressWarnings();
03512     session_start();
03513     wfRestoreWarnings();
03514 }
03515 
03522 function wfGetPrecompiledData( $name ) {
03523     global $IP;
03524 
03525     $file = "$IP/serialized/$name";
03526     if ( file_exists( $file ) ) {
03527         $blob = file_get_contents( $file );
03528         if ( $blob ) {
03529             return unserialize( $blob );
03530         }
03531     }
03532     return false;
03533 }
03534 
03541 function wfMemcKey( /*...*/ ) {
03542     global $wgCachePrefix;
03543     $prefix = $wgCachePrefix === false ? wfWikiID() : $wgCachePrefix;
03544     $args = func_get_args();
03545     $key = $prefix . ':' . implode( ':', $args );
03546     $key = str_replace( ' ', '_', $key );
03547     return $key;
03548 }
03549 
03558 function wfForeignMemcKey( $db, $prefix /*...*/ ) {
03559     $args = array_slice( func_get_args(), 2 );
03560     if ( $prefix ) {
03561         $key = "$db-$prefix:" . implode( ':', $args );
03562     } else {
03563         $key = $db . ':' . implode( ':', $args );
03564     }
03565     return str_replace( ' ', '_', $key );
03566 }
03567 
03574 function wfWikiID() {
03575     global $wgDBprefix, $wgDBname;
03576     if ( $wgDBprefix ) {
03577         return "$wgDBname-$wgDBprefix";
03578     } else {
03579         return $wgDBname;
03580     }
03581 }
03582 
03590 function wfSplitWikiID( $wiki ) {
03591     $bits = explode( '-', $wiki, 2 );
03592     if ( count( $bits ) < 2 ) {
03593         $bits[] = '';
03594     }
03595     return $bits;
03596 }
03597 
03620 function &wfGetDB( $db, $groups = array(), $wiki = false ) {
03621     return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
03622 }
03623 
03630 function wfGetLB( $wiki = false ) {
03631     return wfGetLBFactory()->getMainLB( $wiki );
03632 }
03633 
03639 function &wfGetLBFactory() {
03640     return LBFactory::singleton();
03641 }
03642 
03663 function wfFindFile( $title, $options = array() ) {
03664     return RepoGroup::singleton()->findFile( $title, $options );
03665 }
03666 
03674 function wfLocalFile( $title ) {
03675     return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
03676 }
03677 
03684 function wfQueriesMustScale() {
03685     global $wgMiserMode;
03686     return $wgMiserMode
03687         || ( SiteStats::pages() > 100000
03688         && SiteStats::edits() > 1000000
03689         && SiteStats::users() > 10000 );
03690 }
03691 
03700 function wfScript( $script = 'index' ) {
03701     global $wgScriptPath, $wgScriptExtension, $wgScript, $wgLoadScript;
03702     if ( $script === 'index' ) {
03703         return $wgScript;
03704     } elseif ( $script === 'load' ) {
03705         return $wgLoadScript;
03706     } else {
03707         return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
03708     }
03709 }
03710 
03716 function wfGetScriptUrl() {
03717     if ( isset( $_SERVER['SCRIPT_NAME'] ) ) {
03718         #
03719         # as it was called, minus the query string.
03720         #
03721         # Some sites use Apache rewrite rules to handle subdomains,
03722         # and have PHP set up in a weird way that causes PHP_SELF
03723         # to contain the rewritten URL instead of the one that the
03724         # outside world sees.
03725         #
03726         # If in this mode, use SCRIPT_URL instead, which mod_rewrite
03727         # provides containing the "before" URL.
03728         return $_SERVER['SCRIPT_NAME'];
03729     } else {
03730         return $_SERVER['URL'];
03731     }
03732 }
03733 
03741 function wfBoolToStr( $value ) {
03742     return $value ? 'true' : 'false';
03743 }
03744 
03750 function wfGetNull() {
03751     return wfIsWindows() ? 'NUL' : '/dev/null';
03752 }
03753 
03766 function wfWaitForSlaves( $ifWritesSince = false, $wiki = false, $cluster = false ) {
03767     // B/C: first argument used to be "max seconds of lag"; ignore such values
03768     $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : false;
03769 
03770     if ( $cluster !== false ) {
03771         $lb = wfGetLBFactory()->getExternalLB( $cluster );
03772     } else {
03773         $lb = wfGetLB( $wiki );
03774     }
03775 
03776     // bug 27975 - Don't try to wait for slaves if there are none
03777     // Prevents permission error when getting master position
03778     if ( $lb->getServerCount() > 1 ) {
03779         if ( $ifWritesSince && !$lb->hasMasterConnection() ) {
03780             return true; // assume no writes done
03781         }
03782         $dbw = $lb->getConnection( DB_MASTER, array(), $wiki );
03783         if ( $ifWritesSince && $dbw->lastDoneWrites() < $ifWritesSince ) {
03784             return true; // no writes since the last wait
03785         }
03786         $pos = $dbw->getMasterPos();
03787         // The DBMS may not support getMasterPos() or the whole
03788         // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
03789         if ( $pos !== false ) {
03790             return $lb->waitForAll( $pos, PHP_SAPI === 'cli' ? 86400 : null );
03791         }
03792     }
03793 
03794     return true;
03795 }
03796 
03804 function wfCountDown( $seconds ) {
03805     for ( $i = $seconds; $i >= 0; $i-- ) {
03806         if ( $i != $seconds ) {
03807             echo str_repeat( "\x08", strlen( $i + 1 ) );
03808         }
03809         echo $i;
03810         flush();
03811         if ( $i ) {
03812             sleep( 1 );
03813         }
03814     }
03815     echo "\n";
03816 }
03817 
03826 function wfStripIllegalFilenameChars( $name ) {
03827     global $wgIllegalFileChars;
03828     $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '';
03829     $name = wfBaseName( $name );
03830     $name = preg_replace(
03831         "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/",
03832         '-',
03833         $name
03834     );
03835     return $name;
03836 }
03837 
03843 function wfMemoryLimit() {
03844     global $wgMemoryLimit;
03845     $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
03846     if ( $memlimit != -1 ) {
03847         $conflimit = wfShorthandToInteger( $wgMemoryLimit );
03848         if ( $conflimit == -1 ) {
03849             wfDebug( "Removing PHP's memory limit\n" );
03850             wfSuppressWarnings();
03851             ini_set( 'memory_limit', $conflimit );
03852             wfRestoreWarnings();
03853             return $conflimit;
03854         } elseif ( $conflimit > $memlimit ) {
03855             wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
03856             wfSuppressWarnings();
03857             ini_set( 'memory_limit', $conflimit );
03858             wfRestoreWarnings();
03859             return $conflimit;
03860         }
03861     }
03862     return $memlimit;
03863 }
03864 
03871 function wfShorthandToInteger( $string = '' ) {
03872     $string = trim( $string );
03873     if ( $string === '' ) {
03874         return -1;
03875     }
03876     $last = $string[strlen( $string ) - 1];
03877     $val = intval( $string );
03878     switch ( $last ) {
03879         case 'g':
03880         case 'G':
03881             $val *= 1024;
03882             // break intentionally missing
03883         case 'm':
03884         case 'M':
03885             $val *= 1024;
03886             // break intentionally missing
03887         case 'k':
03888         case 'K':
03889             $val *= 1024;
03890     }
03891 
03892     return $val;
03893 }
03894 
03902 function wfBCP47( $code ) {
03903     $codeSegment = explode( '-', $code );
03904     $codeBCP = array();
03905     foreach ( $codeSegment as $segNo => $seg ) {
03906         // when previous segment is x, it is a private segment and should be lc
03907         if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
03908             $codeBCP[$segNo] = strtolower( $seg );
03909         // ISO 3166 country code
03910         } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
03911             $codeBCP[$segNo] = strtoupper( $seg );
03912         // ISO 15924 script code
03913         } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
03914             $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
03915         // Use lowercase for other cases
03916         } else {
03917             $codeBCP[$segNo] = strtolower( $seg );
03918         }
03919     }
03920     $langCode = implode( '-', $codeBCP );
03921     return $langCode;
03922 }
03923 
03930 function wfGetCache( $inputType ) {
03931     return ObjectCache::getInstance( $inputType );
03932 }
03933 
03939 function wfGetMainCache() {
03940     global $wgMainCacheType;
03941     return ObjectCache::getInstance( $wgMainCacheType );
03942 }
03943 
03949 function wfGetMessageCacheStorage() {
03950     global $wgMessageCacheType;
03951     return ObjectCache::getInstance( $wgMessageCacheType );
03952 }
03953 
03959 function wfGetParserCacheStorage() {
03960     global $wgParserCacheType;
03961     return ObjectCache::getInstance( $wgParserCacheType );
03962 }
03963 
03969 function wfGetLangConverterCacheStorage() {
03970     global $wgLanguageConverterCacheType;
03971     return ObjectCache::getInstance( $wgLanguageConverterCacheType );
03972 }
03973 
03983 function wfRunHooks( $event, array $args = array(), $deprecatedVersion = null ) {
03984     return Hooks::run( $event, $args, $deprecatedVersion );
03985 }
03986 
04001 function wfUnpack( $format, $data, $length = false ) {
04002     if ( $length !== false ) {
04003         $realLen = strlen( $data );
04004         if ( $realLen < $length ) {
04005             throw new MWException( "Tried to use wfUnpack on a "
04006                 . "string of length $realLen, but needed one "
04007                 . "of at least length $length."
04008             );
04009         }
04010     }
04011 
04012     wfSuppressWarnings();
04013     $result = unpack( $format, $data );
04014     wfRestoreWarnings();
04015 
04016     if ( $result === false ) {
04017         // If it cannot extract the packed data.
04018         throw new MWException( "unpack could not unpack binary data" );
04019     }
04020     return $result;
04021 }
04022 
04037 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) {
04038     static $badImageCache = null; // based on bad_image_list msg
04039     wfProfileIn( __METHOD__ );
04040 
04041     # Handle redirects
04042     $redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) );
04043     if ( $redirectTitle ) {
04044         $name = $redirectTitle->getDBkey();
04045     }
04046 
04047     # Run the extension hook
04048     $bad = false;
04049     if ( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) {
04050         wfProfileOut( __METHOD__ );
04051         return $bad;
04052     }
04053 
04054     $cacheable = ( $blacklist === null );
04055     if ( $cacheable && $badImageCache !== null ) {
04056         $badImages = $badImageCache;
04057     } else { // cache miss
04058         if ( $blacklist === null ) {
04059             $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list
04060         }
04061         # Build the list now
04062         $badImages = array();
04063         $lines = explode( "\n", $blacklist );
04064         foreach ( $lines as $line ) {
04065             # List items only
04066             if ( substr( $line, 0, 1 ) !== '*' ) {
04067                 continue;
04068             }
04069 
04070             # Find all links
04071             $m = array();
04072             if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) {
04073                 continue;
04074             }
04075 
04076             $exceptions = array();
04077             $imageDBkey = false;
04078             foreach ( $m[1] as $i => $titleText ) {
04079                 $title = Title::newFromText( $titleText );
04080                 if ( !is_null( $title ) ) {
04081                     if ( $i == 0 ) {
04082                         $imageDBkey = $title->getDBkey();
04083                     } else {
04084                         $exceptions[$title->getPrefixedDBkey()] = true;
04085                     }
04086                 }
04087             }
04088 
04089             if ( $imageDBkey !== false ) {
04090                 $badImages[$imageDBkey] = $exceptions;
04091             }
04092         }
04093         if ( $cacheable ) {
04094             $badImageCache = $badImages;
04095         }
04096     }
04097 
04098     $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false;
04099     $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] );
04100     wfProfileOut( __METHOD__ );
04101     return $bad;
04102 }
04103 
04111 function wfCanIPUseHTTPS( $ip ) {
04112     $canDo = true;
04113     wfRunHooks( 'CanIPUseHTTPS', array( $ip, &$canDo ) );
04114     return !!$canDo;
04115 }
04116 
04124 function wfGetIP() {
04125     wfDeprecated( __METHOD__, '1.19' );
04126     global $wgRequest;
04127     return $wgRequest->getIP();
04128 }
04129 
04139 function wfIsTrustedProxy( $ip ) {
04140     return IP::isTrustedProxy( $ip );
04141 }
04142 
04151 function wfIsConfiguredProxy( $ip ) {
04152     return IP::isConfiguredProxy( $ip );
04153 }