MediaWiki
REL1_23
|
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( 'iconv' ) ) { 00043 function iconv( $from, $to, $string ) { 00044 return Fallback::iconv( $from, $to, $string ); 00045 } 00046 } 00047 00048 if ( !function_exists( 'mb_substr' ) ) { 00053 function mb_substr( $str, $start, $count = 'end' ) { 00054 return Fallback::mb_substr( $str, $start, $count ); 00055 } 00056 00061 function mb_substr_split_unicode( $str, $splitPos ) { 00062 return Fallback::mb_substr_split_unicode( $str, $splitPos ); 00063 } 00064 } 00065 00066 if ( !function_exists( 'mb_strlen' ) ) { 00071 function mb_strlen( $str, $enc = '' ) { 00072 return Fallback::mb_strlen( $str, $enc ); 00073 } 00074 } 00075 00076 if ( !function_exists( 'mb_strpos' ) ) { 00081 function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) { 00082 return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding ); 00083 } 00084 } 00085 00086 if ( !function_exists( 'mb_strrpos' ) ) { 00091 function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) { 00092 return Fallback::mb_strrpos( $haystack, $needle, $offset, $encoding ); 00093 } 00094 } 00095 00096 // gzdecode function only exists in PHP >= 5.4.0 00097 // http://php.net/gzdecode 00098 if ( !function_exists( 'gzdecode' ) ) { 00103 function gzdecode( $data ) { 00104 return gzinflate( substr( $data, 10, -8 ) ); 00105 } 00106 } 00108 00115 function wfArrayDiff2( $a, $b ) { 00116 return array_udiff( $a, $b, 'wfArrayDiff2_cmp' ); 00117 } 00118 00124 function wfArrayDiff2_cmp( $a, $b ) { 00125 if ( is_string( $a ) && is_string( $b ) ) { 00126 return strcmp( $a, $b ); 00127 } elseif ( count( $a ) !== count( $b ) ) { 00128 return count( $a ) < count( $b ) ? -1 : 1; 00129 } else { 00130 reset( $a ); 00131 reset( $b ); 00132 while ( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) { 00133 $cmp = strcmp( $valueA, $valueB ); 00134 if ( $cmp !== 0 ) { 00135 return $cmp; 00136 } 00137 } 00138 return 0; 00139 } 00140 } 00141 00152 function wfArrayLookup( $a, $b ) { 00153 wfDeprecated( __FUNCTION__, '1.22' ); 00154 return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) ); 00155 } 00156 00166 function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) { 00167 if ( is_null( $changed ) ) { 00168 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' ); 00169 } 00170 if ( $default[$key] !== $value ) { 00171 $changed[$key] = $value; 00172 } 00173 } 00174 00185 function wfArrayMerge( $array1 /*...*/ ) { 00186 wfDeprecated( __FUNCTION__, '1.22' ); 00187 $args = func_get_args(); 00188 $args = array_reverse( $args, true ); 00189 $out = array(); 00190 foreach ( $args as $arg ) { 00191 $out += $arg; 00192 } 00193 return $out; 00194 } 00195 00215 function wfMergeErrorArrays( /*...*/ ) { 00216 $args = func_get_args(); 00217 $out = array(); 00218 foreach ( $args as $errors ) { 00219 foreach ( $errors as $params ) { 00220 # @todo FIXME: Sometimes get nested arrays for $params, 00221 # which leads to E_NOTICEs 00222 $spec = implode( "\t", $params ); 00223 $out[$spec] = $params; 00224 } 00225 } 00226 return array_values( $out ); 00227 } 00228 00237 function wfArrayInsertAfter( array $array, array $insert, $after ) { 00238 // Find the offset of the element to insert after. 00239 $keys = array_keys( $array ); 00240 $offsetByKey = array_flip( $keys ); 00241 00242 $offset = $offsetByKey[$after]; 00243 00244 // Insert at the specified offset 00245 $before = array_slice( $array, 0, $offset + 1, true ); 00246 $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true ); 00247 00248 $output = $before + $insert + $after; 00249 00250 return $output; 00251 } 00252 00260 function wfObjectToArray( $objOrArray, $recursive = true ) { 00261 $array = array(); 00262 if ( is_object( $objOrArray ) ) { 00263 $objOrArray = get_object_vars( $objOrArray ); 00264 } 00265 foreach ( $objOrArray as $key => $value ) { 00266 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) { 00267 $value = wfObjectToArray( $value ); 00268 } 00269 00270 $array[$key] = $value; 00271 } 00272 00273 return $array; 00274 } 00275 00283 function wfRandom() { 00284 # The maximum random value is "only" 2^31-1, so get two random 00285 # values to reduce the chance of dupes 00286 $max = mt_getrandmax() + 1; 00287 $rand = number_format( ( mt_rand() * $max + mt_rand() ) / $max / $max, 12, '.', '' ); 00288 00289 return $rand; 00290 } 00291 00302 function wfRandomString( $length = 32 ) { 00303 $str = ''; 00304 for ( $n = 0; $n < $length; $n += 7 ) { 00305 $str .= sprintf( '%07x', mt_rand() & 0xfffffff ); 00306 } 00307 return substr( $str, 0, $length ); 00308 } 00309 00332 function wfUrlencode( $s ) { 00333 static $needle; 00334 00335 if ( is_null( $s ) ) { 00336 $needle = null; 00337 return ''; 00338 } 00339 00340 if ( is_null( $needle ) ) { 00341 $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' ); 00342 if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || 00343 ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) 00344 ) { 00345 $needle[] = '%3A'; 00346 } 00347 } 00348 00349 $s = urlencode( $s ); 00350 $s = str_ireplace( 00351 $needle, 00352 array( ';', '@', '$', '!', '*', '(', ')', ',', '/', ':' ), 00353 $s 00354 ); 00355 00356 return $s; 00357 } 00358 00369 function wfArrayToCgi( $array1, $array2 = null, $prefix = '' ) { 00370 if ( !is_null( $array2 ) ) { 00371 $array1 = $array1 + $array2; 00372 } 00373 00374 $cgi = ''; 00375 foreach ( $array1 as $key => $value ) { 00376 if ( !is_null( $value ) && $value !== false ) { 00377 if ( $cgi != '' ) { 00378 $cgi .= '&'; 00379 } 00380 if ( $prefix !== '' ) { 00381 $key = $prefix . "[$key]"; 00382 } 00383 if ( is_array( $value ) ) { 00384 $firstTime = true; 00385 foreach ( $value as $k => $v ) { 00386 $cgi .= $firstTime ? '' : '&'; 00387 if ( is_array( $v ) ) { 00388 $cgi .= wfArrayToCgi( $v, null, $key . "[$k]" ); 00389 } else { 00390 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v ); 00391 } 00392 $firstTime = false; 00393 } 00394 } else { 00395 if ( is_object( $value ) ) { 00396 $value = $value->__toString(); 00397 } 00398 $cgi .= urlencode( $key ) . '=' . urlencode( $value ); 00399 } 00400 } 00401 } 00402 return $cgi; 00403 } 00404 00414 function wfCgiToArray( $query ) { 00415 if ( isset( $query[0] ) && $query[0] == '?' ) { 00416 $query = substr( $query, 1 ); 00417 } 00418 $bits = explode( '&', $query ); 00419 $ret = array(); 00420 foreach ( $bits as $bit ) { 00421 if ( $bit === '' ) { 00422 continue; 00423 } 00424 if ( strpos( $bit, '=' ) === false ) { 00425 // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does) 00426 $key = $bit; 00427 $value = ''; 00428 } else { 00429 list( $key, $value ) = explode( '=', $bit ); 00430 } 00431 $key = urldecode( $key ); 00432 $value = urldecode( $value ); 00433 if ( strpos( $key, '[' ) !== false ) { 00434 $keys = array_reverse( explode( '[', $key ) ); 00435 $key = array_pop( $keys ); 00436 $temp = $value; 00437 foreach ( $keys as $k ) { 00438 $k = substr( $k, 0, -1 ); 00439 $temp = array( $k => $temp ); 00440 } 00441 if ( isset( $ret[$key] ) ) { 00442 $ret[$key] = array_merge( $ret[$key], $temp ); 00443 } else { 00444 $ret[$key] = $temp; 00445 } 00446 } else { 00447 $ret[$key] = $value; 00448 } 00449 } 00450 return $ret; 00451 } 00452 00461 function wfAppendQuery( $url, $query ) { 00462 if ( is_array( $query ) ) { 00463 $query = wfArrayToCgi( $query ); 00464 } 00465 if ( $query != '' ) { 00466 if ( false === strpos( $url, '?' ) ) { 00467 $url .= '?'; 00468 } else { 00469 $url .= '&'; 00470 } 00471 $url .= $query; 00472 } 00473 return $url; 00474 } 00475 00499 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) { 00500 global $wgServer, $wgCanonicalServer, $wgInternalServer, $wgRequest; 00501 if ( $defaultProto === PROTO_CANONICAL ) { 00502 $serverUrl = $wgCanonicalServer; 00503 } elseif ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) { 00504 // Make $wgInternalServer fall back to $wgServer if not set 00505 $serverUrl = $wgInternalServer; 00506 } else { 00507 $serverUrl = $wgServer; 00508 if ( $defaultProto === PROTO_CURRENT ) { 00509 $defaultProto = $wgRequest->getProtocol() . '://'; 00510 } 00511 } 00512 00513 // Analyze $serverUrl to obtain its protocol 00514 $bits = wfParseUrl( $serverUrl ); 00515 $serverHasProto = $bits && $bits['scheme'] != ''; 00516 00517 if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) { 00518 if ( $serverHasProto ) { 00519 $defaultProto = $bits['scheme'] . '://'; 00520 } else { 00521 // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. 00522 // This really isn't supposed to happen. Fall back to HTTP in this 00523 // ridiculous case. 00524 $defaultProto = PROTO_HTTP; 00525 } 00526 } 00527 00528 $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 ); 00529 00530 if ( substr( $url, 0, 2 ) == '//' ) { 00531 $url = $defaultProtoWithoutSlashes . $url; 00532 } elseif ( substr( $url, 0, 1 ) == '/' ) { 00533 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, 00534 // otherwise leave it alone. 00535 $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url; 00536 } 00537 00538 $bits = wfParseUrl( $url ); 00539 if ( $bits && isset( $bits['path'] ) ) { 00540 $bits['path'] = wfRemoveDotSegments( $bits['path'] ); 00541 return wfAssembleUrl( $bits ); 00542 } elseif ( $bits ) { 00543 # No path to expand 00544 return $url; 00545 } elseif ( substr( $url, 0, 1 ) != '/' ) { 00546 # URL is a relative path 00547 return wfRemoveDotSegments( $url ); 00548 } 00549 00550 # Expanded URL is not valid. 00551 return false; 00552 } 00553 00567 function wfAssembleUrl( $urlParts ) { 00568 $result = ''; 00569 00570 if ( isset( $urlParts['delimiter'] ) ) { 00571 if ( isset( $urlParts['scheme'] ) ) { 00572 $result .= $urlParts['scheme']; 00573 } 00574 00575 $result .= $urlParts['delimiter']; 00576 } 00577 00578 if ( isset( $urlParts['host'] ) ) { 00579 if ( isset( $urlParts['user'] ) ) { 00580 $result .= $urlParts['user']; 00581 if ( isset( $urlParts['pass'] ) ) { 00582 $result .= ':' . $urlParts['pass']; 00583 } 00584 $result .= '@'; 00585 } 00586 00587 $result .= $urlParts['host']; 00588 00589 if ( isset( $urlParts['port'] ) ) { 00590 $result .= ':' . $urlParts['port']; 00591 } 00592 } 00593 00594 if ( isset( $urlParts['path'] ) ) { 00595 $result .= $urlParts['path']; 00596 } 00597 00598 if ( isset( $urlParts['query'] ) ) { 00599 $result .= '?' . $urlParts['query']; 00600 } 00601 00602 if ( isset( $urlParts['fragment'] ) ) { 00603 $result .= '#' . $urlParts['fragment']; 00604 } 00605 00606 return $result; 00607 } 00608 00619 function wfRemoveDotSegments( $urlPath ) { 00620 $output = ''; 00621 $inputOffset = 0; 00622 $inputLength = strlen( $urlPath ); 00623 00624 while ( $inputOffset < $inputLength ) { 00625 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 ); 00626 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 ); 00627 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 ); 00628 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 ); 00629 $trimOutput = false; 00630 00631 if ( $prefixLengthTwo == './' ) { 00632 # Step A, remove leading "./" 00633 $inputOffset += 2; 00634 } elseif ( $prefixLengthThree == '../' ) { 00635 # Step A, remove leading "../" 00636 $inputOffset += 3; 00637 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) { 00638 # Step B, replace leading "/.$" with "/" 00639 $inputOffset += 1; 00640 $urlPath[$inputOffset] = '/'; 00641 } elseif ( $prefixLengthThree == '/./' ) { 00642 # Step B, replace leading "/./" with "/" 00643 $inputOffset += 2; 00644 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) { 00645 # Step C, replace leading "/..$" with "/" and 00646 # remove last path component in output 00647 $inputOffset += 2; 00648 $urlPath[$inputOffset] = '/'; 00649 $trimOutput = true; 00650 } elseif ( $prefixLengthFour == '/../' ) { 00651 # Step C, replace leading "/../" with "/" and 00652 # remove last path component in output 00653 $inputOffset += 3; 00654 $trimOutput = true; 00655 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) { 00656 # Step D, remove "^.$" 00657 $inputOffset += 1; 00658 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) { 00659 # Step D, remove "^..$" 00660 $inputOffset += 2; 00661 } else { 00662 # Step E, move leading path segment to output 00663 if ( $prefixLengthOne == '/' ) { 00664 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 ); 00665 } else { 00666 $slashPos = strpos( $urlPath, '/', $inputOffset ); 00667 } 00668 if ( $slashPos === false ) { 00669 $output .= substr( $urlPath, $inputOffset ); 00670 $inputOffset = $inputLength; 00671 } else { 00672 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset ); 00673 $inputOffset += $slashPos - $inputOffset; 00674 } 00675 } 00676 00677 if ( $trimOutput ) { 00678 $slashPos = strrpos( $output, '/' ); 00679 if ( $slashPos === false ) { 00680 $output = ''; 00681 } else { 00682 $output = substr( $output, 0, $slashPos ); 00683 } 00684 } 00685 } 00686 00687 return $output; 00688 } 00689 00697 function wfUrlProtocols( $includeProtocolRelative = true ) { 00698 global $wgUrlProtocols; 00699 00700 // Cache return values separately based on $includeProtocolRelative 00701 static $withProtRel = null, $withoutProtRel = null; 00702 $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel; 00703 if ( !is_null( $cachedValue ) ) { 00704 return $cachedValue; 00705 } 00706 00707 // Support old-style $wgUrlProtocols strings, for backwards compatibility 00708 // with LocalSettings files from 1.5 00709 if ( is_array( $wgUrlProtocols ) ) { 00710 $protocols = array(); 00711 foreach ( $wgUrlProtocols as $protocol ) { 00712 // Filter out '//' if !$includeProtocolRelative 00713 if ( $includeProtocolRelative || $protocol !== '//' ) { 00714 $protocols[] = preg_quote( $protocol, '/' ); 00715 } 00716 } 00717 00718 $retval = implode( '|', $protocols ); 00719 } else { 00720 // Ignore $includeProtocolRelative in this case 00721 // This case exists for pre-1.6 compatibility, and we can safely assume 00722 // that '//' won't appear in a pre-1.6 config because protocol-relative 00723 // URLs weren't supported until 1.18 00724 $retval = $wgUrlProtocols; 00725 } 00726 00727 // Cache return value 00728 if ( $includeProtocolRelative ) { 00729 $withProtRel = $retval; 00730 } else { 00731 $withoutProtRel = $retval; 00732 } 00733 return $retval; 00734 } 00735 00742 function wfUrlProtocolsWithoutProtRel() { 00743 return wfUrlProtocols( false ); 00744 } 00745 00757 function wfParseUrl( $url ) { 00758 global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php 00759 00760 // Protocol-relative URLs are handled really badly by parse_url(). It's so 00761 // bad that the easiest way to handle them is to just prepend 'http:' and 00762 // strip the protocol out later. 00763 $wasRelative = substr( $url, 0, 2 ) == '//'; 00764 if ( $wasRelative ) { 00765 $url = "http:$url"; 00766 } 00767 wfSuppressWarnings(); 00768 $bits = parse_url( $url ); 00769 wfRestoreWarnings(); 00770 // parse_url() returns an array without scheme for some invalid URLs, e.g. 00771 // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' ) 00772 if ( !$bits || !isset( $bits['scheme'] ) ) { 00773 return false; 00774 } 00775 00776 // parse_url() incorrectly handles schemes case-sensitively. Convert it to lowercase. 00777 $bits['scheme'] = strtolower( $bits['scheme'] ); 00778 00779 // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it 00780 if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) { 00781 $bits['delimiter'] = '://'; 00782 } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) { 00783 $bits['delimiter'] = ':'; 00784 // parse_url detects for news: and mailto: the host part of an url as path 00785 // We have to correct this wrong detection 00786 if ( isset( $bits['path'] ) ) { 00787 $bits['host'] = $bits['path']; 00788 $bits['path'] = ''; 00789 } 00790 } else { 00791 return false; 00792 } 00793 00794 /* Provide an empty host for eg. file:/// urls (see bug 28627) */ 00795 if ( !isset( $bits['host'] ) ) { 00796 $bits['host'] = ''; 00797 00798 // bug 45069 00799 if ( isset( $bits['path'] ) ) { 00800 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */ 00801 if ( substr( $bits['path'], 0, 1 ) !== '/' ) { 00802 $bits['path'] = '/' . $bits['path']; 00803 } 00804 } else { 00805 $bits['path'] = ''; 00806 } 00807 } 00808 00809 // If the URL was protocol-relative, fix scheme and delimiter 00810 if ( $wasRelative ) { 00811 $bits['scheme'] = ''; 00812 $bits['delimiter'] = '//'; 00813 } 00814 return $bits; 00815 } 00816 00827 function wfExpandIRI( $url ) { 00828 return preg_replace_callback( 00829 '/((?:%[89A-F][0-9A-F])+)/i', 00830 'wfExpandIRI_callback', 00831 wfExpandUrl( $url ) 00832 ); 00833 } 00834 00840 function wfExpandIRI_callback( $matches ) { 00841 return urldecode( $matches[1] ); 00842 } 00843 00850 function wfMakeUrlIndexes( $url ) { 00851 $bits = wfParseUrl( $url ); 00852 00853 // Reverse the labels in the hostname, convert to lower case 00854 // For emails reverse domainpart only 00855 if ( $bits['scheme'] == 'mailto' ) { 00856 $mailparts = explode( '@', $bits['host'], 2 ); 00857 if ( count( $mailparts ) === 2 ) { 00858 $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) ); 00859 } else { 00860 // No domain specified, don't mangle it 00861 $domainpart = ''; 00862 } 00863 $reversedHost = $domainpart . '@' . $mailparts[0]; 00864 } else { 00865 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) ); 00866 } 00867 // Add an extra dot to the end 00868 // Why? Is it in wrong place in mailto links? 00869 if ( substr( $reversedHost, -1, 1 ) !== '.' ) { 00870 $reversedHost .= '.'; 00871 } 00872 // Reconstruct the pseudo-URL 00873 $prot = $bits['scheme']; 00874 $index = $prot . $bits['delimiter'] . $reversedHost; 00875 // Leave out user and password. Add the port, path, query and fragment 00876 if ( isset( $bits['port'] ) ) { 00877 $index .= ':' . $bits['port']; 00878 } 00879 if ( isset( $bits['path'] ) ) { 00880 $index .= $bits['path']; 00881 } else { 00882 $index .= '/'; 00883 } 00884 if ( isset( $bits['query'] ) ) { 00885 $index .= '?' . $bits['query']; 00886 } 00887 if ( isset( $bits['fragment'] ) ) { 00888 $index .= '#' . $bits['fragment']; 00889 } 00890 00891 if ( $prot == '' ) { 00892 return array( "http:$index", "https:$index" ); 00893 } else { 00894 return array( $index ); 00895 } 00896 } 00897 00904 function wfMatchesDomainList( $url, $domains ) { 00905 $bits = wfParseUrl( $url ); 00906 if ( is_array( $bits ) && isset( $bits['host'] ) ) { 00907 $host = '.' . $bits['host']; 00908 foreach ( (array)$domains as $domain ) { 00909 $domain = '.' . $domain; 00910 if ( substr( $host, -strlen( $domain ) ) === $domain ) { 00911 return true; 00912 } 00913 } 00914 } 00915 return false; 00916 } 00917 00935 function wfDebug( $text, $dest = 'all' ) { 00936 global $wgDebugLogFile, $wgDebugRawPage, $wgDebugLogPrefix; 00937 00938 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) { 00939 return; 00940 } 00941 00942 // Turn $dest into a string if it's a boolean (for b/c) 00943 if ( $dest === true ) { 00944 $dest = 'all'; 00945 } elseif ( $dest === false ) { 00946 $dest = 'log'; 00947 } 00948 00949 $timer = wfDebugTimer(); 00950 if ( $timer !== '' ) { 00951 $text = preg_replace( '/[^\n]/', $timer . '\0', $text, 1 ); 00952 } 00953 00954 if ( $dest === 'all' ) { 00955 MWDebug::debugMsg( $text ); 00956 } 00957 00958 if ( $wgDebugLogFile != '' ) { 00959 # Strip unprintables; they can switch terminal modes when binary data 00960 # gets dumped, which is pretty annoying. 00961 $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text ); 00962 $text = $wgDebugLogPrefix . $text; 00963 wfErrorLog( $text, $wgDebugLogFile ); 00964 } 00965 } 00966 00971 function wfIsDebugRawPage() { 00972 static $cache; 00973 if ( $cache !== null ) { 00974 return $cache; 00975 } 00976 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet 00977 if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' ) 00978 || ( 00979 isset( $_SERVER['SCRIPT_NAME'] ) 00980 && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php' 00981 ) 00982 ) { 00983 $cache = true; 00984 } else { 00985 $cache = false; 00986 } 00987 return $cache; 00988 } 00989 00995 function wfDebugTimer() { 00996 global $wgDebugTimestamps, $wgRequestTime; 00997 00998 if ( !$wgDebugTimestamps ) { 00999 return ''; 01000 } 01001 01002 $prefix = sprintf( "%6.4f", microtime( true ) - $wgRequestTime ); 01003 $mem = sprintf( "%5.1fM", ( memory_get_usage( true ) / ( 1024 * 1024 ) ) ); 01004 return "$prefix $mem "; 01005 } 01006 01012 function wfDebugMem( $exact = false ) { 01013 $mem = memory_get_usage(); 01014 if ( !$exact ) { 01015 $mem = floor( $mem / 1024 ) . ' kilobytes'; 01016 } else { 01017 $mem .= ' bytes'; 01018 } 01019 wfDebug( "Memory usage: $mem\n" ); 01020 } 01021 01042 function wfDebugLog( $logGroup, $text, $dest = 'all' ) { 01043 global $wgDebugLogGroups; 01044 01045 $text = trim( $text ) . "\n"; 01046 01047 // Turn $dest into a string if it's a boolean (for b/c) 01048 if ( $dest === true ) { 01049 $dest = 'all'; 01050 } elseif ( $dest === false ) { 01051 $dest = 'private'; 01052 } 01053 01054 if ( !isset( $wgDebugLogGroups[$logGroup] ) ) { 01055 if ( $dest !== 'private' ) { 01056 wfDebug( "[$logGroup] $text", $dest ); 01057 } 01058 return; 01059 } 01060 01061 if ( $dest === 'all' ) { 01062 MWDebug::debugMsg( "[$logGroup] $text" ); 01063 } 01064 01065 $logConfig = $wgDebugLogGroups[$logGroup]; 01066 if ( $logConfig === false ) { 01067 return; 01068 } 01069 if ( is_array( $logConfig ) ) { 01070 if ( isset( $logConfig['sample'] ) && mt_rand( 1, $logConfig['sample'] ) !== 1 ) { 01071 return; 01072 } 01073 $destination = $logConfig['destination']; 01074 } else { 01075 $destination = strval( $logConfig ); 01076 } 01077 01078 $time = wfTimestamp( TS_DB ); 01079 $wiki = wfWikiID(); 01080 $host = wfHostname(); 01081 wfErrorLog( "$time $host $wiki: $text", $destination ); 01082 } 01083 01089 function wfLogDBError( $text ) { 01090 global $wgDBerrorLog, $wgDBerrorLogTZ; 01091 static $logDBErrorTimeZoneObject = null; 01092 01093 if ( $wgDBerrorLog ) { 01094 $host = wfHostname(); 01095 $wiki = wfWikiID(); 01096 01097 if ( $wgDBerrorLogTZ && !$logDBErrorTimeZoneObject ) { 01098 $logDBErrorTimeZoneObject = new DateTimeZone( $wgDBerrorLogTZ ); 01099 } 01100 01101 // Workaround for https://bugs.php.net/bug.php?id=52063 01102 // Can be removed when min PHP > 5.3.2 01103 if ( $logDBErrorTimeZoneObject === null ) { 01104 $d = date_create( "now" ); 01105 } else { 01106 $d = date_create( "now", $logDBErrorTimeZoneObject ); 01107 } 01108 01109 $date = $d->format( 'D M j G:i:s T Y' ); 01110 01111 $text = "$date\t$host\t$wiki\t" . trim( $text ) . "\n"; 01112 wfErrorLog( $text, $wgDBerrorLog ); 01113 } 01114 } 01115 01129 function wfDeprecated( $function, $version = false, $component = false, $callerOffset = 2 ) { 01130 MWDebug::deprecated( $function, $version, $component, $callerOffset + 1 ); 01131 } 01132 01143 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) { 01144 MWDebug::warning( $msg, $callerOffset + 1, $level, 'auto' ); 01145 } 01146 01156 function wfLogWarning( $msg, $callerOffset = 1, $level = E_USER_WARNING ) { 01157 MWDebug::warning( $msg, $callerOffset + 1, $level, 'production' ); 01158 } 01159 01170 function wfErrorLog( $text, $file ) { 01171 if ( substr( $file, 0, 4 ) == 'udp:' ) { 01172 # Needs the sockets extension 01173 if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) { 01174 // IPv6 bracketed host 01175 $host = $m[2]; 01176 $port = intval( $m[3] ); 01177 $prefix = isset( $m[4] ) ? $m[4] : false; 01178 $domain = AF_INET6; 01179 } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) { 01180 $host = $m[2]; 01181 if ( !IP::isIPv4( $host ) ) { 01182 $host = gethostbyname( $host ); 01183 } 01184 $port = intval( $m[3] ); 01185 $prefix = isset( $m[4] ) ? $m[4] : false; 01186 $domain = AF_INET; 01187 } else { 01188 throw new MWException( __METHOD__ . ': Invalid UDP specification' ); 01189 } 01190 01191 // Clean it up for the multiplexer 01192 if ( strval( $prefix ) !== '' ) { 01193 $text = preg_replace( '/^/m', $prefix . ' ', $text ); 01194 01195 // Limit to 64KB 01196 if ( strlen( $text ) > 65506 ) { 01197 $text = substr( $text, 0, 65506 ); 01198 } 01199 01200 if ( substr( $text, -1 ) != "\n" ) { 01201 $text .= "\n"; 01202 } 01203 } elseif ( strlen( $text ) > 65507 ) { 01204 $text = substr( $text, 0, 65507 ); 01205 } 01206 01207 $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP ); 01208 if ( !$sock ) { 01209 return; 01210 } 01211 01212 socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port ); 01213 socket_close( $sock ); 01214 } else { 01215 wfSuppressWarnings(); 01216 $exists = file_exists( $file ); 01217 $size = $exists ? filesize( $file ) : false; 01218 if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) { 01219 file_put_contents( $file, $text, FILE_APPEND ); 01220 } 01221 wfRestoreWarnings(); 01222 } 01223 } 01224 01228 function wfLogProfilingData() { 01229 global $wgRequestTime, $wgDebugLogFile, $wgDebugLogGroups, $wgDebugRawPage; 01230 global $wgProfileLimit, $wgUser, $wgRequest; 01231 01232 StatCounter::singleton()->flush(); 01233 01234 $profiler = Profiler::instance(); 01235 01236 # Profiling must actually be enabled... 01237 if ( $profiler->isStub() ) { 01238 return; 01239 } 01240 01241 // Get total page request time and only show pages that longer than 01242 // $wgProfileLimit time (default is 0) 01243 $elapsed = microtime( true ) - $wgRequestTime; 01244 if ( $elapsed <= $wgProfileLimit ) { 01245 return; 01246 } 01247 01248 $profiler->logData(); 01249 01250 // Check whether this should be logged in the debug file. 01251 if ( isset( $wgDebugLogGroups['profileoutput'] ) 01252 && $wgDebugLogGroups['profileoutput'] === false 01253 ) { 01254 // Explicitely disabled 01255 return; 01256 } 01257 if ( !isset( $wgDebugLogGroups['profileoutput'] ) && $wgDebugLogFile == '' ) { 01258 // Logging not enabled; no point going further 01259 return; 01260 } 01261 if ( !$wgDebugRawPage && wfIsDebugRawPage() ) { 01262 return; 01263 } 01264 01265 $forward = ''; 01266 if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) { 01267 $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR']; 01268 } 01269 if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) { 01270 $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP']; 01271 } 01272 if ( !empty( $_SERVER['HTTP_FROM'] ) ) { 01273 $forward .= ' from ' . $_SERVER['HTTP_FROM']; 01274 } 01275 if ( $forward ) { 01276 $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})"; 01277 } 01278 // Don't load $wgUser at this late stage just for statistics purposes 01279 // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId() 01280 if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) { 01281 $forward .= ' anon'; 01282 } 01283 01284 // Command line script uses a FauxRequest object which does not have 01285 // any knowledge about an URL and throw an exception instead. 01286 try { 01287 $requestUrl = $wgRequest->getRequestURL(); 01288 } catch ( MWException $e ) { 01289 $requestUrl = 'n/a'; 01290 } 01291 01292 $log = sprintf( "%s\t%04.3f\t%s\n", 01293 gmdate( 'YmdHis' ), $elapsed, 01294 urldecode( $requestUrl . $forward ) ); 01295 01296 wfDebugLog( 'profileoutput', $log . $profiler->getOutput() ); 01297 } 01298 01306 function wfIncrStats( $key, $count = 1 ) { 01307 StatCounter::singleton()->incr( $key, $count ); 01308 } 01309 01315 function wfReadOnly() { 01316 return wfReadOnlyReason() !== false; 01317 } 01318 01324 function wfReadOnlyReason() { 01325 global $wgReadOnly, $wgReadOnlyFile; 01326 01327 if ( $wgReadOnly === null ) { 01328 // Set $wgReadOnly for faster access next time 01329 if ( is_file( $wgReadOnlyFile ) && filesize( $wgReadOnlyFile ) > 0 ) { 01330 $wgReadOnly = file_get_contents( $wgReadOnlyFile ); 01331 } else { 01332 $wgReadOnly = false; 01333 } 01334 } 01335 01336 return $wgReadOnly; 01337 } 01338 01354 function wfGetLangObj( $langcode = false ) { 01355 # Identify which language to get or create a language object for. 01356 # Using is_object here due to Stub objects. 01357 if ( is_object( $langcode ) ) { 01358 # Great, we already have the object (hopefully)! 01359 return $langcode; 01360 } 01361 01362 global $wgContLang, $wgLanguageCode; 01363 if ( $langcode === true || $langcode === $wgLanguageCode ) { 01364 # $langcode is the language code of the wikis content language object. 01365 # or it is a boolean and value is true 01366 return $wgContLang; 01367 } 01368 01369 global $wgLang; 01370 if ( $langcode === false || $langcode === $wgLang->getCode() ) { 01371 # $langcode is the language code of user language object. 01372 # or it was a boolean and value is false 01373 return $wgLang; 01374 } 01375 01376 $validCodes = array_keys( Language::fetchLanguageNames() ); 01377 if ( in_array( $langcode, $validCodes ) ) { 01378 # $langcode corresponds to a valid language. 01379 return Language::factory( $langcode ); 01380 } 01381 01382 # $langcode is a string, but not a valid language code; use content language. 01383 wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" ); 01384 return $wgContLang; 01385 } 01386 01403 function wfMessage( $key /*...*/ ) { 01404 $params = func_get_args(); 01405 array_shift( $params ); 01406 if ( isset( $params[0] ) && is_array( $params[0] ) ) { 01407 $params = $params[0]; 01408 } 01409 return new Message( $key, $params ); 01410 } 01411 01424 function wfMessageFallback( /*...*/ ) { 01425 $args = func_get_args(); 01426 return call_user_func_array( 'Message::newFallbackSequence', $args ); 01427 } 01428 01448 function wfMsg( $key ) { 01449 wfDeprecated( __METHOD__, '1.21' ); 01450 01451 $args = func_get_args(); 01452 array_shift( $args ); 01453 return wfMsgReal( $key, $args ); 01454 } 01455 01464 function wfMsgNoTrans( $key ) { 01465 wfDeprecated( __METHOD__, '1.21' ); 01466 01467 $args = func_get_args(); 01468 array_shift( $args ); 01469 return wfMsgReal( $key, $args, true, false, false ); 01470 } 01471 01497 function wfMsgForContent( $key ) { 01498 wfDeprecated( __METHOD__, '1.21' ); 01499 01500 global $wgForceUIMsgAsContentMsg; 01501 $args = func_get_args(); 01502 array_shift( $args ); 01503 $forcontent = true; 01504 if ( is_array( $wgForceUIMsgAsContentMsg ) 01505 && in_array( $key, $wgForceUIMsgAsContentMsg ) 01506 ) { 01507 $forcontent = false; 01508 } 01509 return wfMsgReal( $key, $args, true, $forcontent ); 01510 } 01511 01520 function wfMsgForContentNoTrans( $key ) { 01521 wfDeprecated( __METHOD__, '1.21' ); 01522 01523 global $wgForceUIMsgAsContentMsg; 01524 $args = func_get_args(); 01525 array_shift( $args ); 01526 $forcontent = true; 01527 if ( is_array( $wgForceUIMsgAsContentMsg ) 01528 && in_array( $key, $wgForceUIMsgAsContentMsg ) 01529 ) { 01530 $forcontent = false; 01531 } 01532 return wfMsgReal( $key, $args, true, $forcontent, false ); 01533 } 01534 01547 function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) { 01548 wfDeprecated( __METHOD__, '1.21' ); 01549 01550 wfProfileIn( __METHOD__ ); 01551 $message = wfMsgGetKey( $key, $useDB, $forContent, $transform ); 01552 $message = wfMsgReplaceArgs( $message, $args ); 01553 wfProfileOut( __METHOD__ ); 01554 return $message; 01555 } 01556 01569 function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) { 01570 wfDeprecated( __METHOD__, '1.21' ); 01571 01572 wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) ); 01573 01574 $cache = MessageCache::singleton(); 01575 $message = $cache->get( $key, $useDB, $langCode ); 01576 if ( $message === false ) { 01577 $message = '<' . htmlspecialchars( $key ) . '>'; 01578 } elseif ( $transform ) { 01579 $message = $cache->transform( $message ); 01580 } 01581 return $message; 01582 } 01583 01592 function wfMsgReplaceArgs( $message, $args ) { 01593 # Fix windows line-endings 01594 # Some messages are split with explode("\n", $msg) 01595 $message = str_replace( "\r", '', $message ); 01596 01597 // Replace arguments 01598 if ( count( $args ) ) { 01599 if ( is_array( $args[0] ) ) { 01600 $args = array_values( $args[0] ); 01601 } 01602 $replacementKeys = array(); 01603 foreach ( $args as $n => $param ) { 01604 $replacementKeys['$' . ( $n + 1 )] = $param; 01605 } 01606 $message = strtr( $message, $replacementKeys ); 01607 } 01608 01609 return $message; 01610 } 01611 01625 function wfMsgHtml( $key ) { 01626 wfDeprecated( __METHOD__, '1.21' ); 01627 01628 $args = func_get_args(); 01629 array_shift( $args ); 01630 return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args ); 01631 } 01632 01646 function wfMsgWikiHtml( $key ) { 01647 wfDeprecated( __METHOD__, '1.21' ); 01648 01649 $args = func_get_args(); 01650 array_shift( $args ); 01651 return wfMsgReplaceArgs( 01652 MessageCache::singleton()->parse( wfMsgGetKey( $key ), null, 01653 /* can't be set to false */ true, /* interface */ true )->getText(), 01654 $args ); 01655 } 01656 01680 function wfMsgExt( $key, $options ) { 01681 wfDeprecated( __METHOD__, '1.21' ); 01682 01683 $args = func_get_args(); 01684 array_shift( $args ); 01685 array_shift( $args ); 01686 $options = (array)$options; 01687 01688 foreach ( $options as $arrayKey => $option ) { 01689 if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) { 01690 # An unknown index, neither numeric nor "language" 01691 wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING ); 01692 } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, 01693 array( 'parse', 'parseinline', 'escape', 'escapenoentities', 01694 'replaceafter', 'parsemag', 'content' ) ) ) { 01695 # A numeric index with unknown value 01696 wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING ); 01697 } 01698 } 01699 01700 if ( in_array( 'content', $options, true ) ) { 01701 $forContent = true; 01702 $langCode = true; 01703 $langCodeObj = null; 01704 } elseif ( array_key_exists( 'language', $options ) ) { 01705 $forContent = false; 01706 $langCode = wfGetLangObj( $options['language'] ); 01707 $langCodeObj = $langCode; 01708 } else { 01709 $forContent = false; 01710 $langCode = false; 01711 $langCodeObj = null; 01712 } 01713 01714 $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false ); 01715 01716 if ( !in_array( 'replaceafter', $options, true ) ) { 01717 $string = wfMsgReplaceArgs( $string, $args ); 01718 } 01719 01720 $messageCache = MessageCache::singleton(); 01721 $parseInline = in_array( 'parseinline', $options, true ); 01722 if ( in_array( 'parse', $options, true ) || $parseInline ) { 01723 $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj ); 01724 if ( $string instanceof ParserOutput ) { 01725 $string = $string->getText(); 01726 } 01727 01728 if ( $parseInline ) { 01729 $m = array(); 01730 if ( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) { 01731 $string = $m[1]; 01732 } 01733 } 01734 } elseif ( in_array( 'parsemag', $options, true ) ) { 01735 $string = $messageCache->transform( $string, 01736 !$forContent, $langCodeObj ); 01737 } 01738 01739 if ( in_array( 'escape', $options, true ) ) { 01740 $string = htmlspecialchars ( $string ); 01741 } elseif ( in_array( 'escapenoentities', $options, true ) ) { 01742 $string = Sanitizer::escapeHtmlAllowEntities( $string ); 01743 } 01744 01745 if ( in_array( 'replaceafter', $options, true ) ) { 01746 $string = wfMsgReplaceArgs( $string, $args ); 01747 } 01748 01749 return $string; 01750 } 01751 01762 function wfEmptyMsg( $key ) { 01763 wfDeprecated( __METHOD__, '1.21' ); 01764 01765 return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false; 01766 } 01767 01776 function wfDebugDieBacktrace( $msg = '' ) { 01777 wfDeprecated( __FUNCTION__, '1.22' ); 01778 throw new MWException( $msg ); 01779 } 01780 01788 function wfHostname() { 01789 static $host; 01790 if ( is_null( $host ) ) { 01791 01792 # Hostname overriding 01793 global $wgOverrideHostname; 01794 if ( $wgOverrideHostname !== false ) { 01795 # Set static and skip any detection 01796 $host = $wgOverrideHostname; 01797 return $host; 01798 } 01799 01800 if ( function_exists( 'posix_uname' ) ) { 01801 // This function not present on Windows 01802 $uname = posix_uname(); 01803 } else { 01804 $uname = false; 01805 } 01806 if ( is_array( $uname ) && isset( $uname['nodename'] ) ) { 01807 $host = $uname['nodename']; 01808 } elseif ( getenv( 'COMPUTERNAME' ) ) { 01809 # Windows computer name 01810 $host = getenv( 'COMPUTERNAME' ); 01811 } else { 01812 # This may be a virtual server. 01813 $host = $_SERVER['SERVER_NAME']; 01814 } 01815 } 01816 return $host; 01817 } 01818 01828 function wfReportTime() { 01829 global $wgRequestTime, $wgShowHostnames; 01830 01831 $responseTime = round( ( microtime( true ) - $wgRequestTime ) * 1000 ); 01832 $reportVars = array( 'wgBackendResponseTime' => $responseTime ); 01833 if ( $wgShowHostnames ) { 01834 $reportVars[ 'wgHostname' ] = wfHostname(); 01835 } 01836 return Skin::makeVariablesScript( $reportVars ); 01837 } 01838 01854 function wfDebugBacktrace( $limit = 0 ) { 01855 static $disabled = null; 01856 01857 if ( extension_loaded( 'Zend Optimizer' ) ) { 01858 wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" ); 01859 return array(); 01860 } 01861 01862 if ( is_null( $disabled ) ) { 01863 $disabled = false; 01864 $functions = explode( ',', ini_get( 'disable_functions' ) ); 01865 $functions = array_map( 'trim', $functions ); 01866 $functions = array_map( 'strtolower', $functions ); 01867 if ( in_array( 'debug_backtrace', $functions ) ) { 01868 wfDebug( "debug_backtrace is in disabled_functions\n" ); 01869 $disabled = true; 01870 } 01871 } 01872 if ( $disabled ) { 01873 return array(); 01874 } 01875 01876 if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) { 01877 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit + 1 ), 1 ); 01878 } else { 01879 return array_slice( debug_backtrace(), 1 ); 01880 } 01881 } 01882 01888 function wfBacktrace() { 01889 global $wgCommandLineMode; 01890 01891 if ( $wgCommandLineMode ) { 01892 $msg = ''; 01893 } else { 01894 $msg = "<ul>\n"; 01895 } 01896 $backtrace = wfDebugBacktrace(); 01897 foreach ( $backtrace as $call ) { 01898 if ( isset( $call['file'] ) ) { 01899 $f = explode( DIRECTORY_SEPARATOR, $call['file'] ); 01900 $file = $f[count( $f ) - 1]; 01901 } else { 01902 $file = '-'; 01903 } 01904 if ( isset( $call['line'] ) ) { 01905 $line = $call['line']; 01906 } else { 01907 $line = '-'; 01908 } 01909 if ( $wgCommandLineMode ) { 01910 $msg .= "$file line $line calls "; 01911 } else { 01912 $msg .= '<li>' . $file . ' line ' . $line . ' calls '; 01913 } 01914 if ( !empty( $call['class'] ) ) { 01915 $msg .= $call['class'] . $call['type']; 01916 } 01917 $msg .= $call['function'] . '()'; 01918 01919 if ( $wgCommandLineMode ) { 01920 $msg .= "\n"; 01921 } else { 01922 $msg .= "</li>\n"; 01923 } 01924 } 01925 if ( $wgCommandLineMode ) { 01926 $msg .= "\n"; 01927 } else { 01928 $msg .= "</ul>\n"; 01929 } 01930 01931 return $msg; 01932 } 01933 01943 function wfGetCaller( $level = 2 ) { 01944 $backtrace = wfDebugBacktrace( $level + 1 ); 01945 if ( isset( $backtrace[$level] ) ) { 01946 return wfFormatStackFrame( $backtrace[$level] ); 01947 } else { 01948 return 'unknown'; 01949 } 01950 } 01951 01960 function wfGetAllCallers( $limit = 3 ) { 01961 $trace = array_reverse( wfDebugBacktrace() ); 01962 if ( !$limit || $limit > count( $trace ) - 1 ) { 01963 $limit = count( $trace ) - 1; 01964 } 01965 $trace = array_slice( $trace, -$limit - 1, $limit ); 01966 return implode( '/', array_map( 'wfFormatStackFrame', $trace ) ); 01967 } 01968 01975 function wfFormatStackFrame( $frame ) { 01976 return isset( $frame['class'] ) ? 01977 $frame['class'] . '::' . $frame['function'] : 01978 $frame['function']; 01979 } 01980 01981 /* Some generic result counters, pulled out of SearchEngine */ 01982 01990 function wfShowingResults( $offset, $limit ) { 01991 return wfMessage( 'showingresults' )->numParams( $limit, $offset + 1 )->parse(); 01992 } 01993 02005 function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) { 02006 wfDeprecated( __METHOD__, '1.19' ); 02007 02008 global $wgLang; 02009 02010 $query = wfCgiToArray( $query ); 02011 02012 if ( is_object( $link ) ) { 02013 $title = $link; 02014 } else { 02015 $title = Title::newFromText( $link ); 02016 if ( is_null( $title ) ) { 02017 return false; 02018 } 02019 } 02020 02021 return $wgLang->viewPrevNext( $title, $offset, $limit, $query, $atend ); 02022 } 02023 02031 function wfClientAcceptsGzip( $force = false ) { 02032 static $result = null; 02033 if ( $result === null || $force ) { 02034 $result = false; 02035 if ( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) { 02036 # @todo FIXME: We may want to blacklist some broken browsers 02037 $m = array(); 02038 if ( preg_match( 02039 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/', 02040 $_SERVER['HTTP_ACCEPT_ENCODING'], 02041 $m 02042 ) 02043 ) { 02044 if ( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) { 02045 $result = false; 02046 return $result; 02047 } 02048 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" ); 02049 $result = true; 02050 } 02051 } 02052 } 02053 return $result; 02054 } 02055 02065 function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) { 02066 global $wgRequest; 02067 return $wgRequest->getLimitOffset( $deflimit, $optionname ); 02068 } 02069 02079 function wfEscapeWikiText( $text ) { 02080 static $repl = null, $repl2 = null; 02081 if ( $repl === null ) { 02082 $repl = array( 02083 '"' => '"', '&' => '&', "'" => ''', '<' => '<', 02084 '=' => '=', '>' => '>', '[' => '[', ']' => ']', 02085 '{' => '{', '|' => '|', '}' => '}', ';' => ';', 02086 "\n#" => "\n#", "\r#" => "\r#", 02087 "\n*" => "\n*", "\r*" => "\r*", 02088 "\n:" => "\n:", "\r:" => "\r:", 02089 "\n " => "\n ", "\r " => "\r ", 02090 "\n\n" => "\n ", "\r\n" => " \n", 02091 "\n\r" => "\n ", "\r\r" => "\r ", 02092 "\n\t" => "\n	", "\r\t" => "\r	", // "\n\t\n" is treated like "\n\n" 02093 "\n----" => "\n----", "\r----" => "\r----", 02094 '__' => '__', '://' => '://', 02095 ); 02096 02097 // We have to catch everything "\s" matches in PCRE 02098 foreach ( array( 'ISBN', 'RFC', 'PMID' ) as $magic ) { 02099 $repl["$magic "] = "$magic "; 02100 $repl["$magic\t"] = "$magic	"; 02101 $repl["$magic\r"] = "$magic "; 02102 $repl["$magic\n"] = "$magic "; 02103 $repl["$magic\f"] = "$magic"; 02104 } 02105 02106 // And handle protocols that don't use "://" 02107 global $wgUrlProtocols; 02108 $repl2 = array(); 02109 foreach ( $wgUrlProtocols as $prot ) { 02110 if ( substr( $prot, -1 ) === ':' ) { 02111 $repl2[] = preg_quote( substr( $prot, 0, -1 ), '/' ); 02112 } 02113 } 02114 $repl2 = $repl2 ? '/\b(' . join( '|', $repl2 ) . '):/i' : '/^(?!)/'; 02115 } 02116 $text = substr( strtr( "\n$text", $repl ), 1 ); 02117 $text = preg_replace( $repl2, '$1:', $text ); 02118 return $text; 02119 } 02120 02126 function wfTime() { 02127 wfDeprecated( __FUNCTION__, '1.22' ); 02128 return microtime( true ); 02129 } 02130 02141 function wfSetVar( &$dest, $source, $force = false ) { 02142 $temp = $dest; 02143 if ( !is_null( $source ) || $force ) { 02144 $dest = $source; 02145 } 02146 return $temp; 02147 } 02148 02158 function wfSetBit( &$dest, $bit, $state = true ) { 02159 $temp = (bool)( $dest & $bit ); 02160 if ( !is_null( $state ) ) { 02161 if ( $state ) { 02162 $dest |= $bit; 02163 } else { 02164 $dest &= ~$bit; 02165 } 02166 } 02167 return $temp; 02168 } 02169 02176 function wfVarDump( $var ) { 02177 global $wgOut; 02178 $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" ); 02179 if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) { 02180 print $s; 02181 } else { 02182 $wgOut->addHTML( $s ); 02183 } 02184 } 02185 02193 function wfHttpError( $code, $label, $desc ) { 02194 global $wgOut; 02195 $wgOut->disable(); 02196 header( "HTTP/1.0 $code $label" ); 02197 header( "Status: $code $label" ); 02198 $wgOut->sendCacheControl(); 02199 02200 header( 'Content-type: text/html; charset=utf-8' ); 02201 print "<!doctype html>" . 02202 '<html><head><title>' . 02203 htmlspecialchars( $label ) . 02204 '</title></head><body><h1>' . 02205 htmlspecialchars( $label ) . 02206 '</h1><p>' . 02207 nl2br( htmlspecialchars( $desc ) ) . 02208 "</p></body></html>\n"; 02209 } 02210 02228 function wfResetOutputBuffers( $resetGzipEncoding = true ) { 02229 if ( $resetGzipEncoding ) { 02230 // Suppress Content-Encoding and Content-Length 02231 // headers from 1.10+s wfOutputHandler 02232 global $wgDisableOutputCompression; 02233 $wgDisableOutputCompression = true; 02234 } 02235 while ( $status = ob_get_status() ) { 02236 if ( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) { 02237 // Probably from zlib.output_compression or other 02238 // PHP-internal setting which can't be removed. 02239 // 02240 // Give up, and hope the result doesn't break 02241 // output behavior. 02242 break; 02243 } 02244 if ( !ob_end_clean() ) { 02245 // Could not remove output buffer handler; abort now 02246 // to avoid getting in some kind of infinite loop. 02247 break; 02248 } 02249 if ( $resetGzipEncoding ) { 02250 if ( $status['name'] == 'ob_gzhandler' ) { 02251 // Reset the 'Content-Encoding' field set by this handler 02252 // so we can start fresh. 02253 header_remove( 'Content-Encoding' ); 02254 break; 02255 } 02256 } 02257 } 02258 } 02259 02272 function wfClearOutputBuffers() { 02273 wfResetOutputBuffers( false ); 02274 } 02275 02284 function wfAcceptToPrefs( $accept, $def = '*/*' ) { 02285 # No arg means accept anything (per HTTP spec) 02286 if ( !$accept ) { 02287 return array( $def => 1.0 ); 02288 } 02289 02290 $prefs = array(); 02291 02292 $parts = explode( ',', $accept ); 02293 02294 foreach ( $parts as $part ) { 02295 # @todo FIXME: Doesn't deal with params like 'text/html; level=1' 02296 $values = explode( ';', trim( $part ) ); 02297 $match = array(); 02298 if ( count( $values ) == 1 ) { 02299 $prefs[$values[0]] = 1.0; 02300 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) { 02301 $prefs[$values[0]] = floatval( $match[1] ); 02302 } 02303 } 02304 02305 return $prefs; 02306 } 02307 02320 function mimeTypeMatch( $type, $avail ) { 02321 if ( array_key_exists( $type, $avail ) ) { 02322 return $type; 02323 } else { 02324 $parts = explode( '/', $type ); 02325 if ( array_key_exists( $parts[0] . '/*', $avail ) ) { 02326 return $parts[0] . '/*'; 02327 } elseif ( array_key_exists( '*/*', $avail ) ) { 02328 return '*/*'; 02329 } else { 02330 return null; 02331 } 02332 } 02333 } 02334 02348 function wfNegotiateType( $cprefs, $sprefs ) { 02349 $combine = array(); 02350 02351 foreach ( array_keys( $sprefs ) as $type ) { 02352 $parts = explode( '/', $type ); 02353 if ( $parts[1] != '*' ) { 02354 $ckey = mimeTypeMatch( $type, $cprefs ); 02355 if ( $ckey ) { 02356 $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; 02357 } 02358 } 02359 } 02360 02361 foreach ( array_keys( $cprefs ) as $type ) { 02362 $parts = explode( '/', $type ); 02363 if ( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) { 02364 $skey = mimeTypeMatch( $type, $sprefs ); 02365 if ( $skey ) { 02366 $combine[$type] = $sprefs[$skey] * $cprefs[$type]; 02367 } 02368 } 02369 } 02370 02371 $bestq = 0; 02372 $besttype = null; 02373 02374 foreach ( array_keys( $combine ) as $type ) { 02375 if ( $combine[$type] > $bestq ) { 02376 $besttype = $type; 02377 $bestq = $combine[$type]; 02378 } 02379 } 02380 02381 return $besttype; 02382 } 02383 02389 function wfSuppressWarnings( $end = false ) { 02390 static $suppressCount = 0; 02391 static $originalLevel = false; 02392 02393 if ( $end ) { 02394 if ( $suppressCount ) { 02395 --$suppressCount; 02396 if ( !$suppressCount ) { 02397 error_reporting( $originalLevel ); 02398 } 02399 } 02400 } else { 02401 if ( !$suppressCount ) { 02402 $originalLevel = error_reporting( E_ALL & ~( 02403 E_WARNING | 02404 E_NOTICE | 02405 E_USER_WARNING | 02406 E_USER_NOTICE | 02407 E_DEPRECATED | 02408 E_USER_DEPRECATED | 02409 E_STRICT 02410 ) ); 02411 } 02412 ++$suppressCount; 02413 } 02414 } 02415 02419 function wfRestoreWarnings() { 02420 wfSuppressWarnings( true ); 02421 } 02422 02423 # Autodetect, convert and provide timestamps of various types 02424 02428 define( 'TS_UNIX', 0 ); 02429 02433 define( 'TS_MW', 1 ); 02434 02438 define( 'TS_DB', 2 ); 02439 02443 define( 'TS_RFC2822', 3 ); 02444 02450 define( 'TS_ISO_8601', 4 ); 02451 02459 define( 'TS_EXIF', 5 ); 02460 02464 define( 'TS_ORACLE', 6 ); 02465 02469 define( 'TS_POSTGRES', 7 ); 02470 02474 define( 'TS_ISO_8601_BASIC', 9 ); 02475 02485 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) { 02486 try { 02487 $timestamp = new MWTimestamp( $ts ); 02488 return $timestamp->getTimestamp( $outputtype ); 02489 } catch ( TimestampException $e ) { 02490 wfDebug( "wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n" ); 02491 return false; 02492 } 02493 } 02494 02503 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) { 02504 if ( is_null( $ts ) ) { 02505 return null; 02506 } else { 02507 return wfTimestamp( $outputtype, $ts ); 02508 } 02509 } 02510 02516 function wfTimestampNow() { 02517 # return NOW 02518 return wfTimestamp( TS_MW, time() ); 02519 } 02520 02526 function wfIsWindows() { 02527 static $isWindows = null; 02528 if ( $isWindows === null ) { 02529 $isWindows = substr( php_uname(), 0, 7 ) == 'Windows'; 02530 } 02531 return $isWindows; 02532 } 02533 02539 function wfIsHHVM() { 02540 return defined( 'HHVM_VERSION' ); 02541 } 02542 02549 function swap( &$x, &$y ) { 02550 $z = $x; 02551 $x = $y; 02552 $y = $z; 02553 } 02554 02566 function wfTempDir() { 02567 global $wgTmpDirectory; 02568 02569 if ( $wgTmpDirectory !== false ) { 02570 return $wgTmpDirectory; 02571 } 02572 02573 $tmpDir = array_map( "getenv", array( 'TMPDIR', 'TMP', 'TEMP' ) ); 02574 02575 foreach ( $tmpDir as $tmp ) { 02576 if ( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) { 02577 return $tmp; 02578 } 02579 } 02580 return sys_get_temp_dir(); 02581 } 02582 02592 function wfMkdirParents( $dir, $mode = null, $caller = null ) { 02593 global $wgDirectoryMode; 02594 02595 if ( FileBackend::isStoragePath( $dir ) ) { // sanity 02596 throw new MWException( __FUNCTION__ . " given storage path '$dir'." ); 02597 } 02598 02599 if ( !is_null( $caller ) ) { 02600 wfDebug( "$caller: called wfMkdirParents($dir)\n" ); 02601 } 02602 02603 if ( strval( $dir ) === '' || ( file_exists( $dir ) && is_dir( $dir ) ) ) { 02604 return true; 02605 } 02606 02607 $dir = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $dir ); 02608 02609 if ( is_null( $mode ) ) { 02610 $mode = $wgDirectoryMode; 02611 } 02612 02613 // Turn off the normal warning, we're doing our own below 02614 wfSuppressWarnings(); 02615 $ok = mkdir( $dir, $mode, true ); // PHP5 <3 02616 wfRestoreWarnings(); 02617 02618 if ( !$ok ) { 02619 //directory may have been created on another request since we last checked 02620 if ( is_dir( $dir ) ) { 02621 return true; 02622 } 02623 02624 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis. 02625 wfLogWarning( sprintf( "failed to mkdir \"%s\" mode 0%o", $dir, $mode ) ); 02626 } 02627 return $ok; 02628 } 02629 02634 function wfRecursiveRemoveDir( $dir ) { 02635 wfDebug( __FUNCTION__ . "( $dir )\n" ); 02636 // taken from http://de3.php.net/manual/en/function.rmdir.php#98622 02637 if ( is_dir( $dir ) ) { 02638 $objects = scandir( $dir ); 02639 foreach ( $objects as $object ) { 02640 if ( $object != "." && $object != ".." ) { 02641 if ( filetype( $dir . '/' . $object ) == "dir" ) { 02642 wfRecursiveRemoveDir( $dir . '/' . $object ); 02643 } else { 02644 unlink( $dir . '/' . $object ); 02645 } 02646 } 02647 } 02648 reset( $objects ); 02649 rmdir( $dir ); 02650 } 02651 } 02652 02659 function wfPercent( $nr, $acc = 2, $round = true ) { 02660 $ret = sprintf( "%.${acc}f", $nr ); 02661 return $round ? round( $ret, $acc ) . '%' : "$ret%"; 02662 } 02663 02687 function wfIniGetBool( $setting ) { 02688 $val = strtolower( ini_get( $setting ) ); 02689 // 'on' and 'true' can't have whitespace around them, but '1' can. 02690 return $val == 'on' 02691 || $val == 'true' 02692 || $val == 'yes' 02693 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function 02694 } 02695 02707 function wfEscapeShellArg( /*...*/ ) { 02708 wfInitShellLocale(); 02709 02710 $args = func_get_args(); 02711 $first = true; 02712 $retVal = ''; 02713 foreach ( $args as $arg ) { 02714 if ( !$first ) { 02715 $retVal .= ' '; 02716 } else { 02717 $first = false; 02718 } 02719 02720 if ( wfIsWindows() ) { 02721 // Escaping for an MSVC-style command line parser and CMD.EXE 02722 // @codingStandardsIgnoreStart For long URLs 02723 // Refs: 02724 // * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html 02725 // * http://technet.microsoft.com/en-us/library/cc723564.aspx 02726 // * Bug #13518 02727 // * CR r63214 02728 // Double the backslashes before any double quotes. Escape the double quotes. 02729 // @codingStandardsIgnoreEnd 02730 $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE ); 02731 $arg = ''; 02732 $iteration = 0; 02733 foreach ( $tokens as $token ) { 02734 if ( $iteration % 2 == 1 ) { 02735 // Delimiter, a double quote preceded by zero or more slashes 02736 $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"'; 02737 } elseif ( $iteration % 4 == 2 ) { 02738 // ^ in $token will be outside quotes, need to be escaped 02739 $arg .= str_replace( '^', '^^', $token ); 02740 } else { // $iteration % 4 == 0 02741 // ^ in $token will appear inside double quotes, so leave as is 02742 $arg .= $token; 02743 } 02744 $iteration++; 02745 } 02746 // Double the backslashes before the end of the string, because 02747 // we will soon add a quote 02748 $m = array(); 02749 if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) { 02750 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] ); 02751 } 02752 02753 // Add surrounding quotes 02754 $retVal .= '"' . $arg . '"'; 02755 } else { 02756 $retVal .= escapeshellarg( $arg ); 02757 } 02758 } 02759 return $retVal; 02760 } 02761 02768 function wfShellExecDisabled() { 02769 static $disabled = null; 02770 if ( is_null( $disabled ) ) { 02771 $disabled = false; 02772 if ( wfIniGetBool( 'safe_mode' ) ) { 02773 wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" ); 02774 $disabled = 'safemode'; 02775 } else { 02776 $functions = explode( ',', ini_get( 'disable_functions' ) ); 02777 $functions = array_map( 'trim', $functions ); 02778 $functions = array_map( 'strtolower', $functions ); 02779 if ( in_array( 'proc_open', $functions ) ) { 02780 wfDebug( "proc_open is in disabled_functions\n" ); 02781 $disabled = 'disabled'; 02782 } 02783 } 02784 } 02785 return $disabled; 02786 } 02787 02806 function wfShellExec( $cmd, &$retval = null, $environ = array(), 02807 $limits = array(), $options = array() 02808 ) { 02809 global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime, 02810 $wgMaxShellWallClockTime, $wgShellCgroup; 02811 02812 $disabled = wfShellExecDisabled(); 02813 if ( $disabled ) { 02814 $retval = 1; 02815 return $disabled == 'safemode' ? 02816 'Unable to run external programs in safe mode.' : 02817 'Unable to run external programs, proc_open() is disabled.'; 02818 } 02819 02820 $includeStderr = isset( $options['duplicateStderr'] ) && $options['duplicateStderr']; 02821 02822 wfInitShellLocale(); 02823 02824 $envcmd = ''; 02825 foreach ( $environ as $k => $v ) { 02826 if ( wfIsWindows() ) { 02827 /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves 02828 * appear in the environment variable, so we must use carat escaping as documented in 02829 * http://technet.microsoft.com/en-us/library/cc723564.aspx 02830 * Note however that the quote isn't listed there, but is needed, and the parentheses 02831 * are listed there but doesn't appear to need it. 02832 */ 02833 $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& '; 02834 } else { 02835 /* Assume this is a POSIX shell, thus required to accept variable assignments before the command 02836 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01 02837 */ 02838 $envcmd .= "$k=" . escapeshellarg( $v ) . ' '; 02839 } 02840 } 02841 $cmd = $envcmd . $cmd; 02842 02843 $useLogPipe = false; 02844 if ( php_uname( 's' ) == 'Linux' ) { 02845 $time = intval ( isset( $limits['time'] ) ? $limits['time'] : $wgMaxShellTime ); 02846 if ( isset( $limits['walltime'] ) ) { 02847 $wallTime = intval( $limits['walltime'] ); 02848 } elseif ( isset( $limits['time'] ) ) { 02849 $wallTime = $time; 02850 } else { 02851 $wallTime = intval( $wgMaxShellWallClockTime ); 02852 } 02853 $mem = intval ( isset( $limits['memory'] ) ? $limits['memory'] : $wgMaxShellMemory ); 02854 $filesize = intval ( isset( $limits['filesize'] ) ? $limits['filesize'] : $wgMaxShellFileSize ); 02855 02856 if ( $time > 0 || $mem > 0 || $filesize > 0 || $wallTime > 0 ) { 02857 $cmd = '/bin/bash ' . escapeshellarg( "$IP/includes/limit.sh" ) . ' ' . 02858 escapeshellarg( $cmd ) . ' ' . 02859 escapeshellarg( 02860 "MW_INCLUDE_STDERR=" . ( $includeStderr ? '1' : '' ) . ';' . 02861 "MW_CPU_LIMIT=$time; " . 02862 'MW_CGROUP=' . escapeshellarg( $wgShellCgroup ) . '; ' . 02863 "MW_MEM_LIMIT=$mem; " . 02864 "MW_FILE_SIZE_LIMIT=$filesize; " . 02865 "MW_WALL_CLOCK_LIMIT=$wallTime; " . 02866 "MW_USE_LOG_PIPE=yes" 02867 ); 02868 $useLogPipe = true; 02869 } elseif ( $includeStderr ) { 02870 $cmd .= ' 2>&1'; 02871 } 02872 } elseif ( $includeStderr ) { 02873 $cmd .= ' 2>&1'; 02874 } 02875 wfDebug( "wfShellExec: $cmd\n" ); 02876 02877 $desc = array( 02878 0 => array( 'file', 'php://stdin', 'r' ), 02879 1 => array( 'pipe', 'w' ), 02880 2 => array( 'file', 'php://stderr', 'w' ) ); 02881 if ( $useLogPipe ) { 02882 $desc[3] = array( 'pipe', 'w' ); 02883 } 02884 $pipes = null; 02885 $proc = proc_open( $cmd, $desc, $pipes ); 02886 if ( !$proc ) { 02887 wfDebugLog( 'exec', "proc_open() failed: $cmd" ); 02888 $retval = -1; 02889 return ''; 02890 } 02891 $outBuffer = $logBuffer = ''; 02892 $emptyArray = array(); 02893 $status = false; 02894 $logMsg = false; 02895 02896 // According to the documentation, it is possible for stream_select() 02897 // to fail due to EINTR. I haven't managed to induce this in testing 02898 // despite sending various signals. If it did happen, the error 02899 // message would take the form: 02900 // 02901 // stream_select(): unable to select [4]: Interrupted system call (max_fd=5) 02902 // 02903 // where [4] is the value of the macro EINTR and "Interrupted system 02904 // call" is string which according to the Linux manual is "possibly" 02905 // localised according to LC_MESSAGES. 02906 $eintr = defined( 'SOCKET_EINTR' ) ? SOCKET_EINTR : 4; 02907 $eintrMessage = "stream_select(): unable to select [$eintr]"; 02908 02909 // Build a table mapping resource IDs to pipe FDs to work around a 02910 // PHP 5.3 issue in which stream_select() does not preserve array keys 02911 // <https://bugs.php.net/bug.php?id=53427>. 02912 $fds = array(); 02913 foreach ( $pipes as $fd => $pipe ) { 02914 $fds[(int)$pipe] = $fd; 02915 } 02916 02917 $running = true; 02918 $timeout = null; 02919 $numReadyPipes = 0; 02920 02921 while ( $running === true || $numReadyPipes !== 0 ) { 02922 if ( $running ) { 02923 $status = proc_get_status( $proc ); 02924 // If the process has terminated, switch to nonblocking selects 02925 // for getting any data still waiting to be read. 02926 if ( !$status['running'] ) { 02927 $running = false; 02928 $timeout = 0; 02929 } 02930 } 02931 02932 $readyPipes = $pipes; 02933 02934 // Clear last error 02935 @trigger_error( '' ); 02936 $numReadyPipes = @stream_select( $readyPipes, $emptyArray, $emptyArray, $timeout ); 02937 if ( $numReadyPipes === false ) { 02938 $error = error_get_last(); 02939 if ( strncmp( $error['message'], $eintrMessage, strlen( $eintrMessage ) ) == 0 ) { 02940 continue; 02941 } else { 02942 trigger_error( $error['message'], E_USER_WARNING ); 02943 $logMsg = $error['message']; 02944 break; 02945 } 02946 } 02947 foreach ( $readyPipes as $pipe ) { 02948 $block = fread( $pipe, 65536 ); 02949 $fd = $fds[(int)$pipe]; 02950 if ( $block === '' ) { 02951 // End of file 02952 fclose( $pipes[$fd] ); 02953 unset( $pipes[$fd] ); 02954 if ( !$pipes ) { 02955 break 2; 02956 } 02957 } elseif ( $block === false ) { 02958 // Read error 02959 $logMsg = "Error reading from pipe"; 02960 break 2; 02961 } elseif ( $fd == 1 ) { 02962 // From stdout 02963 $outBuffer .= $block; 02964 } elseif ( $fd == 3 ) { 02965 // From log FD 02966 $logBuffer .= $block; 02967 if ( strpos( $block, "\n" ) !== false ) { 02968 $lines = explode( "\n", $logBuffer ); 02969 $logBuffer = array_pop( $lines ); 02970 foreach ( $lines as $line ) { 02971 wfDebugLog( 'exec', $line ); 02972 } 02973 } 02974 } 02975 } 02976 } 02977 02978 foreach ( $pipes as $pipe ) { 02979 fclose( $pipe ); 02980 } 02981 02982 // Use the status previously collected if possible, since proc_get_status() 02983 // just calls waitpid() which will not return anything useful the second time. 02984 if ( $running ) { 02985 $status = proc_get_status( $proc ); 02986 } 02987 02988 if ( $logMsg !== false ) { 02989 // Read/select error 02990 $retval = -1; 02991 proc_close( $proc ); 02992 } elseif ( $status['signaled'] ) { 02993 $logMsg = "Exited with signal {$status['termsig']}"; 02994 $retval = 128 + $status['termsig']; 02995 proc_close( $proc ); 02996 } else { 02997 if ( $status['running'] ) { 02998 $retval = proc_close( $proc ); 02999 } else { 03000 $retval = $status['exitcode']; 03001 proc_close( $proc ); 03002 } 03003 if ( $retval == 127 ) { 03004 $logMsg = "Possibly missing executable file"; 03005 } elseif ( $retval >= 129 && $retval <= 192 ) { 03006 $logMsg = "Probably exited with signal " . ( $retval - 128 ); 03007 } 03008 } 03009 03010 if ( $logMsg !== false ) { 03011 wfDebugLog( 'exec', "$logMsg: $cmd" ); 03012 } 03013 03014 return $outBuffer; 03015 } 03016 03031 function wfShellExecWithStderr( $cmd, &$retval = null, $environ = array(), $limits = array() ) { 03032 return wfShellExec( $cmd, $retval, $environ, $limits, array( 'duplicateStderr' => true ) ); 03033 } 03034 03039 function wfInitShellLocale() { 03040 static $done = false; 03041 if ( $done ) { 03042 return; 03043 } 03044 $done = true; 03045 global $wgShellLocale; 03046 if ( !wfIniGetBool( 'safe_mode' ) ) { 03047 putenv( "LC_CTYPE=$wgShellLocale" ); 03048 setlocale( LC_CTYPE, $wgShellLocale ); 03049 } 03050 } 03051 03057 function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) { 03058 return wfShellWikiCmd( $script, $parameters, $options ); 03059 } 03060 03073 function wfShellWikiCmd( $script, array $parameters = array(), array $options = array() ) { 03074 global $wgPhpCli; 03075 // Give site config file a chance to run the script in a wrapper. 03076 // The caller may likely want to call wfBasename() on $script. 03077 wfRunHooks( 'wfShellWikiCmd', array( &$script, &$parameters, &$options ) ); 03078 $cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli ); 03079 if ( isset( $options['wrapper'] ) ) { 03080 $cmd[] = $options['wrapper']; 03081 } 03082 $cmd[] = $script; 03083 // Escape each parameter for shell 03084 return implode( " ", array_map( 'wfEscapeShellArg', array_merge( $cmd, $parameters ) ) ); 03085 } 03086 03097 function wfMerge( $old, $mine, $yours, &$result ) { 03098 global $wgDiff3; 03099 03100 # This check may also protect against code injection in 03101 # case of broken installations. 03102 wfSuppressWarnings(); 03103 $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 ); 03104 wfRestoreWarnings(); 03105 03106 if ( !$haveDiff3 ) { 03107 wfDebug( "diff3 not found\n" ); 03108 return false; 03109 } 03110 03111 # Make temporary files 03112 $td = wfTempDir(); 03113 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); 03114 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' ); 03115 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' ); 03116 03117 # NOTE: diff3 issues a warning to stderr if any of the files does not end with 03118 # a newline character. To avoid this, we normalize the trailing whitespace before 03119 # creating the diff. 03120 03121 fwrite( $oldtextFile, rtrim( $old ) . "\n" ); 03122 fclose( $oldtextFile ); 03123 fwrite( $mytextFile, rtrim( $mine ) . "\n" ); 03124 fclose( $mytextFile ); 03125 fwrite( $yourtextFile, rtrim( $yours ) . "\n" ); 03126 fclose( $yourtextFile ); 03127 03128 # Check for a conflict 03129 $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a --overlap-only ' . 03130 wfEscapeShellArg( $mytextName ) . ' ' . 03131 wfEscapeShellArg( $oldtextName ) . ' ' . 03132 wfEscapeShellArg( $yourtextName ); 03133 $handle = popen( $cmd, 'r' ); 03134 03135 if ( fgets( $handle, 1024 ) ) { 03136 $conflict = true; 03137 } else { 03138 $conflict = false; 03139 } 03140 pclose( $handle ); 03141 03142 # Merge differences 03143 $cmd = wfEscapeShellArg( $wgDiff3 ) . ' -a -e --merge ' . 03144 wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName ); 03145 $handle = popen( $cmd, 'r' ); 03146 $result = ''; 03147 do { 03148 $data = fread( $handle, 8192 ); 03149 if ( strlen( $data ) == 0 ) { 03150 break; 03151 } 03152 $result .= $data; 03153 } while ( true ); 03154 pclose( $handle ); 03155 unlink( $mytextName ); 03156 unlink( $oldtextName ); 03157 unlink( $yourtextName ); 03158 03159 if ( $result === '' && $old !== '' && !$conflict ) { 03160 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" ); 03161 $conflict = true; 03162 } 03163 return !$conflict; 03164 } 03165 03175 function wfDiff( $before, $after, $params = '-u' ) { 03176 if ( $before == $after ) { 03177 return ''; 03178 } 03179 03180 global $wgDiff; 03181 wfSuppressWarnings(); 03182 $haveDiff = $wgDiff && file_exists( $wgDiff ); 03183 wfRestoreWarnings(); 03184 03185 # This check may also protect against code injection in 03186 # case of broken installations. 03187 if ( !$haveDiff ) { 03188 wfDebug( "diff executable not found\n" ); 03189 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) ); 03190 $format = new UnifiedDiffFormatter(); 03191 return $format->format( $diffs ); 03192 } 03193 03194 # Make temporary files 03195 $td = wfTempDir(); 03196 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' ); 03197 $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' ); 03198 03199 fwrite( $oldtextFile, $before ); 03200 fclose( $oldtextFile ); 03201 fwrite( $newtextFile, $after ); 03202 fclose( $newtextFile ); 03203 03204 // Get the diff of the two files 03205 $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName ); 03206 03207 $h = popen( $cmd, 'r' ); 03208 03209 $diff = ''; 03210 03211 do { 03212 $data = fread( $h, 8192 ); 03213 if ( strlen( $data ) == 0 ) { 03214 break; 03215 } 03216 $diff .= $data; 03217 } while ( true ); 03218 03219 // Clean up 03220 pclose( $h ); 03221 unlink( $oldtextName ); 03222 unlink( $newtextName ); 03223 03224 // Kill the --- and +++ lines. They're not useful. 03225 $diff_lines = explode( "\n", $diff ); 03226 if ( strpos( $diff_lines[0], '---' ) === 0 ) { 03227 unset( $diff_lines[0] ); 03228 } 03229 if ( strpos( $diff_lines[1], '+++' ) === 0 ) { 03230 unset( $diff_lines[1] ); 03231 } 03232 03233 $diff = implode( "\n", $diff_lines ); 03234 03235 return $diff; 03236 } 03237 03254 function wfUsePHP( $req_ver ) { 03255 $php_ver = PHP_VERSION; 03256 03257 if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) { 03258 throw new MWException( "PHP $req_ver required--this is only $php_ver" ); 03259 } 03260 } 03261 03284 function wfUseMW( $req_ver ) { 03285 global $wgVersion; 03286 03287 if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) { 03288 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" ); 03289 } 03290 } 03291 03304 function wfBaseName( $path, $suffix = '' ) { 03305 if ( $suffix == '' ) { 03306 $encSuffix = ''; 03307 } else { 03308 $encSuffix = '(?:' . preg_quote( $suffix, '#' ) . ')?'; 03309 } 03310 03311 $matches = array(); 03312 if ( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) { 03313 return $matches[1]; 03314 } else { 03315 return ''; 03316 } 03317 } 03318 03328 function wfRelativePath( $path, $from ) { 03329 // Normalize mixed input on Windows... 03330 $path = str_replace( '/', DIRECTORY_SEPARATOR, $path ); 03331 $from = str_replace( '/', DIRECTORY_SEPARATOR, $from ); 03332 03333 // Trim trailing slashes -- fix for drive root 03334 $path = rtrim( $path, DIRECTORY_SEPARATOR ); 03335 $from = rtrim( $from, DIRECTORY_SEPARATOR ); 03336 03337 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) ); 03338 $against = explode( DIRECTORY_SEPARATOR, $from ); 03339 03340 if ( $pieces[0] !== $against[0] ) { 03341 // Non-matching Windows drive letters? 03342 // Return a full path. 03343 return $path; 03344 } 03345 03346 // Trim off common prefix 03347 while ( count( $pieces ) && count( $against ) 03348 && $pieces[0] == $against[0] ) { 03349 array_shift( $pieces ); 03350 array_shift( $against ); 03351 } 03352 03353 // relative dots to bump us to the parent 03354 while ( count( $against ) ) { 03355 array_unshift( $pieces, '..' ); 03356 array_shift( $against ); 03357 } 03358 03359 array_push( $pieces, wfBaseName( $path ) ); 03360 03361 return implode( DIRECTORY_SEPARATOR, $pieces ); 03362 } 03363 03379 function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, 03380 $lowercase = true, $engine = 'auto' 03381 ) { 03382 $input = (string)$input; 03383 if ( 03384 $sourceBase < 2 || 03385 $sourceBase > 36 || 03386 $destBase < 2 || 03387 $destBase > 36 || 03388 $sourceBase != (int)$sourceBase || 03389 $destBase != (int)$destBase || 03390 $pad != (int)$pad || 03391 !preg_match( 03392 "/^[" . substr( '0123456789abcdefghijklmnopqrstuvwxyz', 0, $sourceBase ) . "]+$/i", 03393 $input 03394 ) 03395 ) { 03396 return false; 03397 } 03398 03399 static $baseChars = array( 03400 10 => 'a', 11 => 'b', 12 => 'c', 13 => 'd', 14 => 'e', 15 => 'f', 03401 16 => 'g', 17 => 'h', 18 => 'i', 19 => 'j', 20 => 'k', 21 => 'l', 03402 22 => 'm', 23 => 'n', 24 => 'o', 25 => 'p', 26 => 'q', 27 => 'r', 03403 28 => 's', 29 => 't', 30 => 'u', 31 => 'v', 32 => 'w', 33 => 'x', 03404 34 => 'y', 35 => 'z', 03405 03406 '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, 03407 '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'a' => 10, 'b' => 11, 03408 'c' => 12, 'd' => 13, 'e' => 14, 'f' => 15, 'g' => 16, 'h' => 17, 03409 'i' => 18, 'j' => 19, 'k' => 20, 'l' => 21, 'm' => 22, 'n' => 23, 03410 'o' => 24, 'p' => 25, 'q' => 26, 'r' => 27, 's' => 28, 't' => 29, 03411 'u' => 30, 'v' => 31, 'w' => 32, 'x' => 33, 'y' => 34, 'z' => 35 03412 ); 03413 03414 if ( extension_loaded( 'gmp' ) && ( $engine == 'auto' || $engine == 'gmp' ) ) { 03415 $result = gmp_strval( gmp_init( $input, $sourceBase ), $destBase ); 03416 } elseif ( extension_loaded( 'bcmath' ) && ( $engine == 'auto' || $engine == 'bcmath' ) ) { 03417 $decimal = '0'; 03418 foreach ( str_split( strtolower( $input ) ) as $char ) { 03419 $decimal = bcmul( $decimal, $sourceBase ); 03420 $decimal = bcadd( $decimal, $baseChars[$char] ); 03421 } 03422 03423 for ( $result = ''; bccomp( $decimal, 0 ); $decimal = bcdiv( $decimal, $destBase, 0 ) ) { 03424 $result .= $baseChars[bcmod( $decimal, $destBase )]; 03425 } 03426 03427 $result = strrev( $result ); 03428 } else { 03429 $inDigits = array(); 03430 foreach ( str_split( strtolower( $input ) ) as $char ) { 03431 $inDigits[] = $baseChars[$char]; 03432 } 03433 03434 // Iterate over the input, modulo-ing out an output digit 03435 // at a time until input is gone. 03436 $result = ''; 03437 while ( $inDigits ) { 03438 $work = 0; 03439 $workDigits = array(); 03440 03441 // Long division... 03442 foreach ( $inDigits as $digit ) { 03443 $work *= $sourceBase; 03444 $work += $digit; 03445 03446 if ( $workDigits || $work >= $destBase ) { 03447 $workDigits[] = (int)( $work / $destBase ); 03448 } 03449 $work %= $destBase; 03450 } 03451 03452 // All that division leaves us with a remainder, 03453 // which is conveniently our next output digit. 03454 $result .= $baseChars[$work]; 03455 03456 // And we continue! 03457 $inDigits = $workDigits; 03458 } 03459 03460 $result = strrev( $result ); 03461 } 03462 03463 if ( !$lowercase ) { 03464 $result = strtoupper( $result ); 03465 } 03466 03467 return str_pad( $result, $pad, '0', STR_PAD_LEFT ); 03468 } 03469 03475 function wfCheckEntropy() { 03476 return ( 03477 ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) ) 03478 || ini_get( 'session.entropy_file' ) 03479 ) 03480 && intval( ini_get( 'session.entropy_length' ) ) >= 32; 03481 } 03482 03487 function wfFixSessionID() { 03488 // If the cookie or session id is already set we already have a session and should abort 03489 if ( isset( $_COOKIE[session_name()] ) || session_id() ) { 03490 return; 03491 } 03492 03493 // PHP's built-in session entropy is enabled if: 03494 // - entropy_file is set or you're on Windows with php 5.3.3+ 03495 // - AND entropy_length is > 0 03496 // We treat it as disabled if it doesn't have an entropy length of at least 32 03497 $entropyEnabled = wfCheckEntropy(); 03498 03499 // If built-in entropy is not enabled or not sufficient override PHP's 03500 // built in session id generation code 03501 if ( !$entropyEnabled ) { 03502 wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " . 03503 "overriding session id generation using our cryptrand source.\n" ); 03504 session_id( MWCryptRand::generateHex( 32 ) ); 03505 } 03506 } 03507 03513 function wfResetSessionID() { 03514 global $wgCookieSecure; 03515 $oldSessionId = session_id(); 03516 $cookieParams = session_get_cookie_params(); 03517 if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) { 03518 session_regenerate_id( false ); 03519 } else { 03520 $tmp = $_SESSION; 03521 session_destroy(); 03522 wfSetupSession( MWCryptRand::generateHex( 32 ) ); 03523 $_SESSION = $tmp; 03524 } 03525 $newSessionId = session_id(); 03526 wfRunHooks( 'ResetSessionID', array( $oldSessionId, $newSessionId ) ); 03527 } 03528 03534 function wfSetupSession( $sessionId = false ) { 03535 global $wgSessionsInMemcached, $wgSessionsInObjectCache, $wgCookiePath, $wgCookieDomain, 03536 $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler; 03537 if ( $wgSessionsInObjectCache || $wgSessionsInMemcached ) { 03538 ObjectCacheSessionHandler::install(); 03539 } elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) { 03540 # Only set this if $wgSessionHandler isn't null and session.save_handler 03541 # hasn't already been set to the desired value (that causes errors) 03542 ini_set( 'session.save_handler', $wgSessionHandler ); 03543 } 03544 session_set_cookie_params( 03545 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly ); 03546 session_cache_limiter( 'private, must-revalidate' ); 03547 if ( $sessionId ) { 03548 session_id( $sessionId ); 03549 } else { 03550 wfFixSessionID(); 03551 } 03552 wfSuppressWarnings(); 03553 session_start(); 03554 wfRestoreWarnings(); 03555 } 03556 03563 function wfGetPrecompiledData( $name ) { 03564 global $IP; 03565 03566 $file = "$IP/serialized/$name"; 03567 if ( file_exists( $file ) ) { 03568 $blob = file_get_contents( $file ); 03569 if ( $blob ) { 03570 return unserialize( $blob ); 03571 } 03572 } 03573 return false; 03574 } 03575 03582 function wfMemcKey( /*...*/ ) { 03583 global $wgCachePrefix; 03584 $prefix = $wgCachePrefix === false ? wfWikiID() : $wgCachePrefix; 03585 $args = func_get_args(); 03586 $key = $prefix . ':' . implode( ':', $args ); 03587 $key = str_replace( ' ', '_', $key ); 03588 return $key; 03589 } 03590 03599 function wfForeignMemcKey( $db, $prefix /*...*/ ) { 03600 $args = array_slice( func_get_args(), 2 ); 03601 if ( $prefix ) { 03602 $key = "$db-$prefix:" . implode( ':', $args ); 03603 } else { 03604 $key = $db . ':' . implode( ':', $args ); 03605 } 03606 return str_replace( ' ', '_', $key ); 03607 } 03608 03615 function wfWikiID() { 03616 global $wgDBprefix, $wgDBname; 03617 if ( $wgDBprefix ) { 03618 return "$wgDBname-$wgDBprefix"; 03619 } else { 03620 return $wgDBname; 03621 } 03622 } 03623 03631 function wfSplitWikiID( $wiki ) { 03632 $bits = explode( '-', $wiki, 2 ); 03633 if ( count( $bits ) < 2 ) { 03634 $bits[] = ''; 03635 } 03636 return $bits; 03637 } 03638 03661 function &wfGetDB( $db, $groups = array(), $wiki = false ) { 03662 return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki ); 03663 } 03664 03671 function wfGetLB( $wiki = false ) { 03672 return wfGetLBFactory()->getMainLB( $wiki ); 03673 } 03674 03680 function &wfGetLBFactory() { 03681 return LBFactory::singleton(); 03682 } 03683 03704 function wfFindFile( $title, $options = array() ) { 03705 return RepoGroup::singleton()->findFile( $title, $options ); 03706 } 03707 03715 function wfLocalFile( $title ) { 03716 return RepoGroup::singleton()->getLocalRepo()->newFile( $title ); 03717 } 03718 03725 function wfQueriesMustScale() { 03726 global $wgMiserMode; 03727 return $wgMiserMode 03728 || ( SiteStats::pages() > 100000 03729 && SiteStats::edits() > 1000000 03730 && SiteStats::users() > 10000 ); 03731 } 03732 03741 function wfScript( $script = 'index' ) { 03742 global $wgScriptPath, $wgScriptExtension, $wgScript, $wgLoadScript; 03743 if ( $script === 'index' ) { 03744 return $wgScript; 03745 } elseif ( $script === 'load' ) { 03746 return $wgLoadScript; 03747 } else { 03748 return "{$wgScriptPath}/{$script}{$wgScriptExtension}"; 03749 } 03750 } 03751 03757 function wfGetScriptUrl() { 03758 if ( isset( $_SERVER['SCRIPT_NAME'] ) ) { 03759 # 03760 # as it was called, minus the query string. 03761 # 03762 # Some sites use Apache rewrite rules to handle subdomains, 03763 # and have PHP set up in a weird way that causes PHP_SELF 03764 # to contain the rewritten URL instead of the one that the 03765 # outside world sees. 03766 # 03767 # If in this mode, use SCRIPT_URL instead, which mod_rewrite 03768 # provides containing the "before" URL. 03769 return $_SERVER['SCRIPT_NAME']; 03770 } else { 03771 return $_SERVER['URL']; 03772 } 03773 } 03774 03782 function wfBoolToStr( $value ) { 03783 return $value ? 'true' : 'false'; 03784 } 03785 03791 function wfGetNull() { 03792 return wfIsWindows() ? 'NUL' : '/dev/null'; 03793 } 03794 03806 function wfWaitForSlaves( $maxLag = false, $wiki = false, $cluster = false ) { 03807 if ( $cluster !== false ) { 03808 $lb = wfGetLBFactory()->getExternalLB( $cluster ); 03809 } else { 03810 $lb = wfGetLB( $wiki ); 03811 } 03812 03813 // bug 27975 - Don't try to wait for slaves if there are none 03814 // Prevents permission error when getting master position 03815 if ( $lb->getServerCount() > 1 ) { 03816 $dbw = $lb->getConnection( DB_MASTER, array(), $wiki ); 03817 $pos = $dbw->getMasterPos(); 03818 // The DBMS may not support getMasterPos() or the whole 03819 // load balancer might be fake (e.g. $wgAllDBsAreLocalhost). 03820 if ( $pos !== false ) { 03821 $lb->waitForAll( $pos ); 03822 } 03823 } 03824 } 03825 03833 function wfCountDown( $seconds ) { 03834 for ( $i = $seconds; $i >= 0; $i-- ) { 03835 if ( $i != $seconds ) { 03836 echo str_repeat( "\x08", strlen( $i + 1 ) ); 03837 } 03838 echo $i; 03839 flush(); 03840 if ( $i ) { 03841 sleep( 1 ); 03842 } 03843 } 03844 echo "\n"; 03845 } 03846 03855 function wfStripIllegalFilenameChars( $name ) { 03856 global $wgIllegalFileChars; 03857 $illegalFileChars = $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : ''; 03858 $name = wfBaseName( $name ); 03859 $name = preg_replace( 03860 "/[^" . Title::legalChars() . "]" . $illegalFileChars . "/", 03861 '-', 03862 $name 03863 ); 03864 return $name; 03865 } 03866 03872 function wfMemoryLimit() { 03873 global $wgMemoryLimit; 03874 $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) ); 03875 if ( $memlimit != -1 ) { 03876 $conflimit = wfShorthandToInteger( $wgMemoryLimit ); 03877 if ( $conflimit == -1 ) { 03878 wfDebug( "Removing PHP's memory limit\n" ); 03879 wfSuppressWarnings(); 03880 ini_set( 'memory_limit', $conflimit ); 03881 wfRestoreWarnings(); 03882 return $conflimit; 03883 } elseif ( $conflimit > $memlimit ) { 03884 wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" ); 03885 wfSuppressWarnings(); 03886 ini_set( 'memory_limit', $conflimit ); 03887 wfRestoreWarnings(); 03888 return $conflimit; 03889 } 03890 } 03891 return $memlimit; 03892 } 03893 03900 function wfShorthandToInteger( $string = '' ) { 03901 $string = trim( $string ); 03902 if ( $string === '' ) { 03903 return -1; 03904 } 03905 $last = $string[strlen( $string ) - 1]; 03906 $val = intval( $string ); 03907 switch ( $last ) { 03908 case 'g': 03909 case 'G': 03910 $val *= 1024; 03911 // break intentionally missing 03912 case 'm': 03913 case 'M': 03914 $val *= 1024; 03915 // break intentionally missing 03916 case 'k': 03917 case 'K': 03918 $val *= 1024; 03919 } 03920 03921 return $val; 03922 } 03923 03931 function wfBCP47( $code ) { 03932 $codeSegment = explode( '-', $code ); 03933 $codeBCP = array(); 03934 foreach ( $codeSegment as $segNo => $seg ) { 03935 // when previous segment is x, it is a private segment and should be lc 03936 if ( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) { 03937 $codeBCP[$segNo] = strtolower( $seg ); 03938 // ISO 3166 country code 03939 } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) { 03940 $codeBCP[$segNo] = strtoupper( $seg ); 03941 // ISO 15924 script code 03942 } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) { 03943 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) ); 03944 // Use lowercase for other cases 03945 } else { 03946 $codeBCP[$segNo] = strtolower( $seg ); 03947 } 03948 } 03949 $langCode = implode( '-', $codeBCP ); 03950 return $langCode; 03951 } 03952 03959 function wfGetCache( $inputType ) { 03960 return ObjectCache::getInstance( $inputType ); 03961 } 03962 03968 function wfGetMainCache() { 03969 global $wgMainCacheType; 03970 return ObjectCache::getInstance( $wgMainCacheType ); 03971 } 03972 03978 function wfGetMessageCacheStorage() { 03979 global $wgMessageCacheType; 03980 return ObjectCache::getInstance( $wgMessageCacheType ); 03981 } 03982 03988 function wfGetParserCacheStorage() { 03989 global $wgParserCacheType; 03990 return ObjectCache::getInstance( $wgParserCacheType ); 03991 } 03992 03998 function wfGetLangConverterCacheStorage() { 03999 global $wgLanguageConverterCacheType; 04000 return ObjectCache::getInstance( $wgLanguageConverterCacheType ); 04001 } 04002 04012 function wfRunHooks( $event, array $args = array(), $deprecatedVersion = null ) { 04013 return Hooks::run( $event, $args, $deprecatedVersion ); 04014 } 04015 04030 function wfUnpack( $format, $data, $length = false ) { 04031 if ( $length !== false ) { 04032 $realLen = strlen( $data ); 04033 if ( $realLen < $length ) { 04034 throw new MWException( "Tried to use wfUnpack on a " 04035 . "string of length $realLen, but needed one " 04036 . "of at least length $length." 04037 ); 04038 } 04039 } 04040 04041 wfSuppressWarnings(); 04042 $result = unpack( $format, $data ); 04043 wfRestoreWarnings(); 04044 04045 if ( $result === false ) { 04046 // If it cannot extract the packed data. 04047 throw new MWException( "unpack could not unpack binary data" ); 04048 } 04049 return $result; 04050 } 04051 04066 function wfIsBadImage( $name, $contextTitle = false, $blacklist = null ) { 04067 static $badImageCache = null; // based on bad_image_list msg 04068 wfProfileIn( __METHOD__ ); 04069 04070 # Handle redirects 04071 $redirectTitle = RepoGroup::singleton()->checkRedirect( Title::makeTitle( NS_FILE, $name ) ); 04072 if ( $redirectTitle ) { 04073 $name = $redirectTitle->getDBkey(); 04074 } 04075 04076 # Run the extension hook 04077 $bad = false; 04078 if ( !wfRunHooks( 'BadImage', array( $name, &$bad ) ) ) { 04079 wfProfileOut( __METHOD__ ); 04080 return $bad; 04081 } 04082 04083 $cacheable = ( $blacklist === null ); 04084 if ( $cacheable && $badImageCache !== null ) { 04085 $badImages = $badImageCache; 04086 } else { // cache miss 04087 if ( $blacklist === null ) { 04088 $blacklist = wfMessage( 'bad_image_list' )->inContentLanguage()->plain(); // site list 04089 } 04090 # Build the list now 04091 $badImages = array(); 04092 $lines = explode( "\n", $blacklist ); 04093 foreach ( $lines as $line ) { 04094 # List items only 04095 if ( substr( $line, 0, 1 ) !== '*' ) { 04096 continue; 04097 } 04098 04099 # Find all links 04100 $m = array(); 04101 if ( !preg_match_all( '/\[\[:?(.*?)\]\]/', $line, $m ) ) { 04102 continue; 04103 } 04104 04105 $exceptions = array(); 04106 $imageDBkey = false; 04107 foreach ( $m[1] as $i => $titleText ) { 04108 $title = Title::newFromText( $titleText ); 04109 if ( !is_null( $title ) ) { 04110 if ( $i == 0 ) { 04111 $imageDBkey = $title->getDBkey(); 04112 } else { 04113 $exceptions[$title->getPrefixedDBkey()] = true; 04114 } 04115 } 04116 } 04117 04118 if ( $imageDBkey !== false ) { 04119 $badImages[$imageDBkey] = $exceptions; 04120 } 04121 } 04122 if ( $cacheable ) { 04123 $badImageCache = $badImages; 04124 } 04125 } 04126 04127 $contextKey = $contextTitle ? $contextTitle->getPrefixedDBkey() : false; 04128 $bad = isset( $badImages[$name] ) && !isset( $badImages[$name][$contextKey] ); 04129 wfProfileOut( __METHOD__ ); 04130 return $bad; 04131 } 04132 04140 function wfCanIPUseHTTPS( $ip ) { 04141 $canDo = true; 04142 wfRunHooks( 'CanIPUseHTTPS', array( $ip, &$canDo ) ); 04143 return !!$canDo; 04144 } 04145 04153 function wfGetIP() { 04154 wfDeprecated( __METHOD__, '1.19' ); 04155 global $wgRequest; 04156 return $wgRequest->getIP(); 04157 } 04158 04167 function wfIsTrustedProxy( $ip ) { 04168 $trusted = wfIsConfiguredProxy( $ip ); 04169 wfRunHooks( 'IsTrustedProxy', array( &$ip, &$trusted ) ); 04170 return $trusted; 04171 } 04172 04180 function wfIsConfiguredProxy( $ip ) { 04181 global $wgSquidServers, $wgSquidServersNoPurge; 04182 04183 // quick check of known proxy servers 04184 $trusted = in_array( $ip, $wgSquidServers ) 04185 || in_array( $ip, $wgSquidServersNoPurge ); 04186 04187 if ( !$trusted ) { 04188 // slightly slower check to see if the ip is listed directly or in a CIDR 04189 // block in $wgSquidServersNoPurge 04190 foreach ( $wgSquidServersNoPurge as $block ) { 04191 if ( strpos( $block, '/' ) !== false && IP::isInRange( $ip, $block ) ) { 04192 $trusted = true; 04193 break; 04194 } 04195 } 04196 } 04197 return $trusted; 04198 }