MediaWiki  REL1_23
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( '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 = '&lt;' . htmlspecialchars( $key ) . '&gt;';
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             '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
02084             '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
02085             '{' => '&#123;', '|' => '&#124;', '}' => '&#125;', ';' => '&#59;',
02086             "\n#" => "\n&#35;", "\r#" => "\r&#35;",
02087             "\n*" => "\n&#42;", "\r*" => "\r&#42;",
02088             "\n:" => "\n&#58;", "\r:" => "\r&#58;",
02089             "\n " => "\n&#32;", "\r " => "\r&#32;",
02090             "\n\n" => "\n&#10;", "\r\n" => "&#13;\n",
02091             "\n\r" => "\n&#13;", "\r\r" => "\r&#13;",
02092             "\n\t" => "\n&#9;", "\r\t" => "\r&#9;", // "\n\t\n" is treated like "\n\n"
02093             "\n----" => "\n&#45;---", "\r----" => "\r&#45;---",
02094             '__' => '_&#95;', '://' => '&#58;//',
02095         );
02096 
02097         // We have to catch everything "\s" matches in PCRE
02098         foreach ( array( 'ISBN', 'RFC', 'PMID' ) as $magic ) {
02099             $repl["$magic "] = "$magic&#32;";
02100             $repl["$magic\t"] = "$magic&#9;";
02101             $repl["$magic\r"] = "$magic&#13;";
02102             $repl["$magic\n"] = "$magic&#10;";
02103             $repl["$magic\f"] = "$magic&#12;";
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&#58;', $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 }