MediaWiki  REL1_19
GlobalFunctions.php
Go to the documentation of this file.
00001 <?php
00007 if ( !defined( 'MEDIAWIKI' ) ) {
00008         die( "This file is part of MediaWiki, it is not a valid entry point" );
00009 }
00010 
00011 // Hide compatibility functions from Doxygen
00013 
00022 if( !function_exists( 'iconv' ) ) {
00024         function iconv( $from, $to, $string ) {
00025                 return Fallback::iconv( $from, $to, $string );
00026         }
00027 }
00028 
00029 if ( !function_exists( 'mb_substr' ) ) {
00031         function mb_substr( $str, $start, $count='end' ) {
00032                 return Fallback::mb_substr( $str, $start, $count );
00033         }
00034 
00036         function mb_substr_split_unicode( $str, $splitPos ) {
00037                 return Fallback::mb_substr_split_unicode( $str, $splitPos );
00038         }
00039 }
00040 
00041 if ( !function_exists( 'mb_strlen' ) ) {
00043         function mb_strlen( $str, $enc = '' ) {
00044                 return Fallback::mb_strlen( $str, $enc );
00045         }
00046 }
00047 
00048 if( !function_exists( 'mb_strpos' ) ) {
00050         function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
00051                 return Fallback::mb_strpos( $haystack, $needle, $offset, $encoding );
00052         }
00053 
00054 }
00055 
00056 if( !function_exists( 'mb_strrpos' ) ) {
00058         function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
00059                 return Fallback::mb_strrpos( $haystack, $needle, $offset, $encoding );
00060         }
00061 }
00062 
00063 
00064 // Support for Wietse Venema's taint feature
00065 if ( !function_exists( 'istainted' ) ) {
00067         function istainted( $var ) {
00068                 return 0;
00069         }
00071         function taint( $var, $level = 0 ) {}
00073         function untaint( $var, $level = 0 ) {}
00074         define( 'TC_HTML', 1 );
00075         define( 'TC_SHELL', 1 );
00076         define( 'TC_MYSQL', 1 );
00077         define( 'TC_PCRE', 1 );
00078         define( 'TC_SELF', 1 );
00079 }
00081 
00088 function wfArrayDiff2( $a, $b ) {
00089         return array_udiff( $a, $b, 'wfArrayDiff2_cmp' );
00090 }
00091 
00097 function wfArrayDiff2_cmp( $a, $b ) {
00098         if ( !is_array( $a ) ) {
00099                 return strcmp( $a, $b );
00100         } elseif ( count( $a ) !== count( $b ) ) {
00101                 return count( $a ) < count( $b ) ? -1 : 1;
00102         } else {
00103                 reset( $a );
00104                 reset( $b );
00105                 while( ( list( , $valueA ) = each( $a ) ) && ( list( , $valueB ) = each( $b ) ) ) {
00106                         $cmp = strcmp( $valueA, $valueB );
00107                         if ( $cmp !== 0 ) {
00108                                 return $cmp;
00109                         }
00110                 }
00111                 return 0;
00112         }
00113 }
00114 
00124 function wfArrayLookup( $a, $b ) {
00125         return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) );
00126 }
00127 
00136 function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
00137         if ( is_null( $changed ) ) {
00138                 throw new MWException( 'GlobalFunctions::wfAppendToArrayIfNotDefault got null' );
00139         }
00140         if ( $default[$key] !== $value ) {
00141                 $changed[$key] = $value;
00142         }
00143 }
00144 
00153 function wfArrayMerge( $array1/* ... */ ) {
00154         $args = func_get_args();
00155         $args = array_reverse( $args, true );
00156         $out = array();
00157         foreach ( $args as $arg ) {
00158                 $out += $arg;
00159         }
00160         return $out;
00161 }
00162 
00181 function wfMergeErrorArrays( /*...*/ ) {
00182         $args = func_get_args();
00183         $out = array();
00184         foreach ( $args as $errors ) {
00185                 foreach ( $errors as $params ) {
00186                         # @todo FIXME: Sometimes get nested arrays for $params,
00187                         # which leads to E_NOTICEs
00188                         $spec = implode( "\t", $params );
00189                         $out[$spec] = $params;
00190                 }
00191         }
00192         return array_values( $out );
00193 }
00194 
00203 function wfArrayInsertAfter( $array, $insert, $after ) {
00204         // Find the offset of the element to insert after.
00205         $keys = array_keys( $array );
00206         $offsetByKey = array_flip( $keys );
00207 
00208         $offset = $offsetByKey[$after];
00209 
00210         // Insert at the specified offset
00211         $before = array_slice( $array, 0, $offset + 1, true );
00212         $after = array_slice( $array, $offset + 1, count( $array ) - $offset, true );
00213 
00214         $output = $before + $insert + $after;
00215 
00216         return $output;
00217 }
00218 
00226 function wfObjectToArray( $objOrArray, $recursive = true ) {
00227         $array = array();
00228         if( is_object( $objOrArray ) ) {
00229                 $objOrArray = get_object_vars( $objOrArray );
00230         }
00231         foreach ( $objOrArray as $key => $value ) {
00232                 if ( $recursive && ( is_object( $value ) || is_array( $value ) ) ) {
00233                         $value = wfObjectToArray( $value );
00234                 }
00235 
00236                 $array[$key] = $value;
00237         }
00238 
00239         return $array;
00240 }
00241 
00249 function wfArrayMap( $function, $input ) {
00250         $ret = array_map( $function, $input );
00251         foreach ( $ret as $key => $value ) {
00252                 $taint = istainted( $input[$key] );
00253                 if ( $taint ) {
00254                         taint( $ret[$key], $taint );
00255                 }
00256         }
00257         return $ret;
00258 }
00259 
00267 function wfRandom() {
00268         # The maximum random value is "only" 2^31-1, so get two random
00269         # values to reduce the chance of dupes
00270         $max = mt_getrandmax() + 1;
00271         $rand = number_format( ( mt_rand() * $max + mt_rand() )
00272                 / $max / $max, 12, '.', '' );
00273         return $rand;
00274 }
00275 
00298 function wfUrlencode( $s ) {
00299         static $needle;
00300         if ( is_null( $s ) ) {
00301                 $needle = null;
00302                 return '';
00303         }
00304 
00305         if ( is_null( $needle ) ) {
00306                 $needle = array( '%3B', '%40', '%24', '%21', '%2A', '%28', '%29', '%2C', '%2F' );
00307                 if ( !isset( $_SERVER['SERVER_SOFTWARE'] ) || ( strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/7' ) === false ) ) {
00308                         $needle[] = '%3A';
00309                 }
00310         }
00311 
00312         $s = urlencode( $s );
00313         $s = str_ireplace(
00314                 $needle,
00315                 array( ';', '@', '$', '!', '*', '(', ')', ',', '/', ':' ),
00316                 $s
00317         );
00318 
00319         return $s;
00320 }
00321 
00332 function wfArrayToCGI( $array1, $array2 = null, $prefix = '' ) {
00333         if ( !is_null( $array2 ) ) {
00334                 $array1 = $array1 + $array2;
00335         }
00336 
00337         $cgi = '';
00338         foreach ( $array1 as $key => $value ) {
00339                 if ( !is_null($value) && $value !== false ) {
00340                         if ( $cgi != '' ) {
00341                                 $cgi .= '&';
00342                         }
00343                         if ( $prefix !== '' ) {
00344                                 $key = $prefix . "[$key]";
00345                         }
00346                         if ( is_array( $value ) ) {
00347                                 $firstTime = true;
00348                                 foreach ( $value as $k => $v ) {
00349                                         $cgi .= $firstTime ? '' : '&';
00350                                         if ( is_array( $v ) ) {
00351                                                 $cgi .= wfArrayToCGI( $v, null, $key . "[$k]" );
00352                                         } else {
00353                                                 $cgi .= urlencode( $key . "[$k]" ) . '=' . urlencode( $v );
00354                                         }
00355                                         $firstTime = false;
00356                                 }
00357                         } else {
00358                                 if ( is_object( $value ) ) {
00359                                         $value = $value->__toString();
00360                                 }
00361                                 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
00362                         }
00363                 }
00364         }
00365         return $cgi;
00366 }
00367 
00377 function wfCgiToArray( $query ) {
00378         if ( isset( $query[0] ) && $query[0] == '?' ) {
00379                 $query = substr( $query, 1 );
00380         }
00381         $bits = explode( '&', $query );
00382         $ret = array();
00383         foreach ( $bits as $bit ) {
00384                 if ( $bit === '' ) {
00385                         continue;
00386                 }
00387                 if ( strpos( $bit, '=' ) === false ) {
00388                         // Pieces like &qwerty become 'qwerty' => '' (at least this is what php does)
00389                         $key = $bit;
00390                         $value = '';
00391                 } else {
00392                         list( $key, $value ) = explode( '=', $bit );
00393                 }
00394                 $key = urldecode( $key );
00395                 $value = urldecode( $value );
00396                 if ( strpos( $key, '[' ) !== false ) {
00397                         $keys = array_reverse( explode( '[', $key ) );
00398                         $key = array_pop( $keys );
00399                         $temp = $value;
00400                         foreach ( $keys as $k ) {
00401                                 $k = substr( $k, 0, -1 );
00402                                 $temp = array( $k => $temp );
00403                         }
00404                         if ( isset( $ret[$key] ) ) {
00405                                 $ret[$key] = array_merge( $ret[$key], $temp );
00406                         } else {
00407                                 $ret[$key] = $temp;
00408                         }
00409                 } else {
00410                         $ret[$key] = $value;
00411                 }
00412         }
00413         return $ret;
00414 }
00415 
00424 function wfAppendQuery( $url, $query ) {
00425         if ( is_array( $query ) ) {
00426                 $query = wfArrayToCGI( $query );
00427         }
00428         if( $query != '' ) {
00429                 if( false === strpos( $url, '?' ) ) {
00430                         $url .= '?';
00431                 } else {
00432                         $url .= '&';
00433                 }
00434                 $url .= $query;
00435         }
00436         return $url;
00437 }
00438 
00461 function wfExpandUrl( $url, $defaultProto = PROTO_CURRENT ) {
00462         global $wgServer, $wgCanonicalServer, $wgInternalServer;
00463         $serverUrl = $wgServer;
00464         if ( $defaultProto === PROTO_CANONICAL ) {
00465                 $serverUrl = $wgCanonicalServer;
00466         }
00467         // Make $wgInternalServer fall back to $wgServer if not set
00468         if ( $defaultProto === PROTO_INTERNAL && $wgInternalServer !== false ) {
00469                 $serverUrl = $wgInternalServer;
00470         }
00471         if ( $defaultProto === PROTO_CURRENT ) {
00472                 $defaultProto = WebRequest::detectProtocol() . '://';
00473         }
00474 
00475         // Analyze $serverUrl to obtain its protocol
00476         $bits = wfParseUrl( $serverUrl );
00477         $serverHasProto = $bits && $bits['scheme'] != '';
00478 
00479         if ( $defaultProto === PROTO_CANONICAL || $defaultProto === PROTO_INTERNAL ) {
00480                 if ( $serverHasProto ) {
00481                         $defaultProto = $bits['scheme'] . '://';
00482                 } else {
00483                         // $wgCanonicalServer or $wgInternalServer doesn't have a protocol. This really isn't supposed to happen
00484                         // Fall back to HTTP in this ridiculous case
00485                         $defaultProto = PROTO_HTTP;
00486                 }
00487         }
00488 
00489         $defaultProtoWithoutSlashes = substr( $defaultProto, 0, -2 );
00490 
00491         if ( substr( $url, 0, 2 ) == '//' ) {
00492                 $url = $defaultProtoWithoutSlashes . $url;
00493         } elseif ( substr( $url, 0, 1 ) == '/' ) {
00494                 // If $serverUrl is protocol-relative, prepend $defaultProtoWithoutSlashes, otherwise leave it alone
00495                 $url = ( $serverHasProto ? '' : $defaultProtoWithoutSlashes ) . $serverUrl . $url;
00496         }
00497 
00498         $bits = wfParseUrl( $url );
00499         if ( $bits && isset( $bits['path'] ) ) {
00500                 $bits['path'] = wfRemoveDotSegments( $bits['path'] );
00501                 return wfAssembleUrl( $bits );
00502         } elseif ( $bits ) {
00503                 # No path to expand
00504                 return $url;
00505         } elseif ( substr( $url, 0, 1 ) != '/' ) {
00506                 # URL is a relative path
00507                 return wfRemoveDotSegments( $url );
00508         }
00509 
00510         # Expanded URL is not valid.
00511         return false;
00512 }
00513 
00527 function wfAssembleUrl( $urlParts ) {
00528         $result = '';
00529 
00530         if ( isset( $urlParts['delimiter'] ) ) {
00531                 if ( isset( $urlParts['scheme'] ) ) {
00532                         $result .= $urlParts['scheme'];
00533                 }
00534 
00535                 $result .= $urlParts['delimiter'];
00536         }
00537 
00538         if ( isset( $urlParts['host'] ) ) {
00539                 if ( isset( $urlParts['user'] ) ) {
00540                         $result .= $urlParts['user'];
00541                         if ( isset( $urlParts['pass'] ) ) {
00542                                 $result .= ':' . $urlParts['pass'];
00543                         }
00544                         $result .= '@';
00545                 }
00546 
00547                 $result .= $urlParts['host'];
00548 
00549                 if ( isset( $urlParts['port'] ) ) {
00550                         $result .= ':' . $urlParts['port'];
00551                 }
00552         }
00553 
00554         if ( isset( $urlParts['path'] ) ) {
00555                 $result .= $urlParts['path'];
00556         }
00557 
00558         if ( isset( $urlParts['query'] ) ) {
00559                 $result .= '?' . $urlParts['query'];
00560         }
00561 
00562         if ( isset( $urlParts['fragment'] ) ) {
00563                 $result .= '#' . $urlParts['fragment'];
00564         }
00565 
00566         return $result;
00567 }
00568 
00579 function wfRemoveDotSegments( $urlPath ) {
00580         $output = '';
00581         $inputOffset = 0;
00582         $inputLength = strlen( $urlPath );
00583 
00584         while ( $inputOffset < $inputLength ) {
00585                 $prefixLengthOne = substr( $urlPath, $inputOffset, 1 );
00586                 $prefixLengthTwo = substr( $urlPath, $inputOffset, 2 );
00587                 $prefixLengthThree = substr( $urlPath, $inputOffset, 3 );
00588                 $prefixLengthFour = substr( $urlPath, $inputOffset, 4 );
00589                 $trimOutput = false;
00590 
00591                 if ( $prefixLengthTwo == './' ) {
00592                         # Step A, remove leading "./"
00593                         $inputOffset += 2;
00594                 } elseif ( $prefixLengthThree == '../' ) {
00595                         # Step A, remove leading "../"
00596                         $inputOffset += 3;
00597                 } elseif ( ( $prefixLengthTwo == '/.' ) && ( $inputOffset + 2 == $inputLength ) ) {
00598                         # Step B, replace leading "/.$" with "/"
00599                         $inputOffset += 1;
00600                         $urlPath[$inputOffset] = '/';
00601                 } elseif ( $prefixLengthThree == '/./' ) {
00602                         # Step B, replace leading "/./" with "/"
00603                         $inputOffset += 2;
00604                 } elseif ( $prefixLengthThree == '/..' && ( $inputOffset + 3 == $inputLength ) ) {
00605                         # Step C, replace leading "/..$" with "/" and
00606                         # remove last path component in output
00607                         $inputOffset += 2;
00608                         $urlPath[$inputOffset] = '/';
00609                         $trimOutput = true;
00610                 } elseif ( $prefixLengthFour == '/../' ) {
00611                         # Step C, replace leading "/../" with "/" and
00612                         # remove last path component in output
00613                         $inputOffset += 3;
00614                         $trimOutput = true;
00615                 } elseif ( ( $prefixLengthOne == '.' ) && ( $inputOffset + 1 == $inputLength ) ) {
00616                         # Step D, remove "^.$"
00617                         $inputOffset += 1;
00618                 } elseif ( ( $prefixLengthTwo == '..' ) && ( $inputOffset + 2 == $inputLength ) ) {
00619                         # Step D, remove "^..$"
00620                         $inputOffset += 2;
00621                 } else {
00622                         # Step E, move leading path segment to output
00623                         if ( $prefixLengthOne == '/' ) {
00624                                 $slashPos = strpos( $urlPath, '/', $inputOffset + 1 );
00625                         } else {
00626                                 $slashPos = strpos( $urlPath, '/', $inputOffset );
00627                         }
00628                         if ( $slashPos === false ) {
00629                                 $output .= substr( $urlPath, $inputOffset );
00630                                 $inputOffset = $inputLength;
00631                         } else {
00632                                 $output .= substr( $urlPath, $inputOffset, $slashPos - $inputOffset );
00633                                 $inputOffset += $slashPos - $inputOffset;
00634                         }
00635                 }
00636 
00637                 if ( $trimOutput ) {
00638                         $slashPos = strrpos( $output, '/' );
00639                         if ( $slashPos === false ) {
00640                                 $output = '';
00641                         } else {
00642                                 $output = substr( $output, 0, $slashPos );
00643                         }
00644                 }
00645         }
00646 
00647         return $output;
00648 }
00649 
00657 function wfUrlProtocols( $includeProtocolRelative = true ) {
00658         global $wgUrlProtocols;
00659 
00660         // Cache return values separately based on $includeProtocolRelative
00661         static $withProtRel = null, $withoutProtRel = null;
00662         $cachedValue = $includeProtocolRelative ? $withProtRel : $withoutProtRel;
00663         if ( !is_null( $cachedValue ) ) {
00664                 return $cachedValue;
00665         }
00666 
00667         // Support old-style $wgUrlProtocols strings, for backwards compatibility
00668         // with LocalSettings files from 1.5
00669         if ( is_array( $wgUrlProtocols ) ) {
00670                 $protocols = array();
00671                 foreach ( $wgUrlProtocols as $protocol ) {
00672                         // Filter out '//' if !$includeProtocolRelative
00673                         if ( $includeProtocolRelative || $protocol !== '//' ) {
00674                                 $protocols[] = preg_quote( $protocol, '/' );
00675                         }
00676                 }
00677 
00678                 $retval = implode( '|', $protocols );
00679         } else {
00680                 // Ignore $includeProtocolRelative in this case
00681                 // This case exists for pre-1.6 compatibility, and we can safely assume
00682                 // that '//' won't appear in a pre-1.6 config because protocol-relative
00683                 // URLs weren't supported until 1.18
00684                 $retval = $wgUrlProtocols;
00685         }
00686 
00687         // Cache return value
00688         if ( $includeProtocolRelative ) {
00689                 $withProtRel = $retval;
00690         } else {
00691                 $withoutProtRel = $retval;
00692         }
00693         return $retval;
00694 }
00695 
00702 function wfUrlProtocolsWithoutProtRel() {
00703         return wfUrlProtocols( false );
00704 }
00705 
00716 function wfParseUrl( $url ) {
00717         global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
00718 
00719         // Protocol-relative URLs are handled really badly by parse_url(). It's so bad that the easiest
00720         // way to handle them is to just prepend 'http:' and strip the protocol out later
00721         $wasRelative = substr( $url, 0, 2 ) == '//';
00722         if ( $wasRelative ) {
00723                 $url = "http:$url";
00724         }
00725         wfSuppressWarnings();
00726         $bits = parse_url( $url );
00727         wfRestoreWarnings();
00728         // parse_url() returns an array without scheme for some invalid URLs, e.g.
00729         // parse_url("%0Ahttp://example.com") == array( 'host' => '%0Ahttp', 'path' => 'example.com' )
00730         if ( !$bits || !isset( $bits['scheme'] ) ) {
00731                 return false;
00732         }
00733 
00734         // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
00735         if ( in_array( $bits['scheme'] . '://', $wgUrlProtocols ) ) {
00736                 $bits['delimiter'] = '://';
00737         } elseif ( in_array( $bits['scheme'] . ':', $wgUrlProtocols ) ) {
00738                 $bits['delimiter'] = ':';
00739                 // parse_url detects for news: and mailto: the host part of an url as path
00740                 // We have to correct this wrong detection
00741                 if ( isset( $bits['path'] ) ) {
00742                         $bits['host'] = $bits['path'];
00743                         $bits['path'] = '';
00744                 }
00745         } else {
00746                 return false;
00747         }
00748 
00749         /* Provide an empty host for eg. file:/// urls (see bug 28627) */
00750         if ( !isset( $bits['host'] ) ) {
00751                 $bits['host'] = '';
00752 
00753                 /* parse_url loses the third / for file:///c:/ urls (but not on variants) */
00754                 if ( substr( $bits['path'], 0, 1 ) !== '/' ) {
00755                         $bits['path'] = '/' . $bits['path'];
00756                 }
00757         }
00758 
00759         // If the URL was protocol-relative, fix scheme and delimiter
00760         if ( $wasRelative ) {
00761                 $bits['scheme'] = '';
00762                 $bits['delimiter'] = '//';
00763         }
00764         return $bits;
00765 }
00766 
00773 function wfMakeUrlIndexes( $url ) {
00774         $bits = wfParseUrl( $url );
00775 
00776         // Reverse the labels in the hostname, convert to lower case
00777         // For emails reverse domainpart only
00778         if ( $bits['scheme'] == 'mailto' ) {
00779                 $mailparts = explode( '@', $bits['host'], 2 );
00780                 if ( count( $mailparts ) === 2 ) {
00781                         $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
00782                 } else {
00783                         // No domain specified, don't mangle it
00784                         $domainpart = '';
00785                 }
00786                 $reversedHost = $domainpart . '@' . $mailparts[0];
00787         } else {
00788                 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
00789         }
00790         // Add an extra dot to the end
00791         // Why? Is it in wrong place in mailto links?
00792         if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
00793                 $reversedHost .= '.';
00794         }
00795         // Reconstruct the pseudo-URL
00796         $prot = $bits['scheme'];
00797         $index = $prot . $bits['delimiter'] . $reversedHost;
00798         // Leave out user and password. Add the port, path, query and fragment
00799         if ( isset( $bits['port'] ) ) {
00800                 $index .= ':' . $bits['port'];
00801         }
00802         if ( isset( $bits['path'] ) ) {
00803                 $index .= $bits['path'];
00804         } else {
00805                 $index .= '/';
00806         }
00807         if ( isset( $bits['query'] ) ) {
00808                 $index .= '?' . $bits['query'];
00809         }
00810         if ( isset( $bits['fragment'] ) ) {
00811                 $index .= '#' . $bits['fragment'];
00812         }
00813 
00814         if ( $prot == '' ) {
00815                 return array( "http:$index", "https:$index" );
00816         } else {
00817                 return array( $index );
00818         }
00819 }
00820 
00827 function wfMatchesDomainList( $url, $domains ) {
00828         $bits = wfParseUrl( $url );
00829         if ( is_array( $bits ) && isset( $bits['host'] ) ) {
00830                 foreach ( (array)$domains as $domain ) {
00831                         // FIXME: This gives false positives. http://nds-nl.wikipedia.org will match nl.wikipedia.org
00832                         // We should use something that interprets dots instead
00833                         if ( substr( $bits['host'], -strlen( $domain ) ) === $domain ) {
00834                                 return true;
00835                         }
00836                 }
00837         }
00838         return false;
00839 }
00840 
00854 function wfDebug( $text, $logonly = false ) {
00855         global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
00856         global $wgDebugLogPrefix, $wgShowDebug;
00857 
00858         static $cache = array(); // Cache of unoutputted messages
00859         $text = wfDebugTimer() . $text;
00860 
00861         if ( !$wgDebugRawPage && wfIsDebugRawPage() ) {
00862                 return;
00863         }
00864 
00865         if ( ( $wgDebugComments || $wgShowDebug ) && !$logonly ) {
00866                 $cache[] = $text;
00867 
00868                 if ( isset( $wgOut ) && is_object( $wgOut ) ) {
00869                         // add the message and any cached messages to the output
00870                         array_map( array( $wgOut, 'debug' ), $cache );
00871                         $cache = array();
00872                 }
00873         }
00874         if ( wfRunHooks( 'Debug', array( $text, null /* no log group */ ) ) ) {
00875                 if ( $wgDebugLogFile != '' && !$wgProfileOnly ) {
00876                         # Strip unprintables; they can switch terminal modes when binary data
00877                         # gets dumped, which is pretty annoying.
00878                         $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
00879                         $text = $wgDebugLogPrefix . $text;
00880                         wfErrorLog( $text, $wgDebugLogFile );
00881                 }
00882         }
00883 
00884         MWDebug::debugMsg( $text );
00885 }
00886 
00890 function wfIsDebugRawPage() {
00891         static $cache;
00892         if ( $cache !== null ) {
00893                 return $cache;
00894         }
00895         # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
00896         if ( ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' )
00897                 || (
00898                         isset( $_SERVER['SCRIPT_NAME'] )
00899                         && substr( $_SERVER['SCRIPT_NAME'], -8 ) == 'load.php'
00900                 ) )
00901         {
00902                 $cache = true;
00903         } else {
00904                 $cache = false;
00905         }
00906         return $cache;
00907 }
00908 
00914 function wfDebugTimer() {
00915         global $wgDebugTimestamps, $wgRequestTime;
00916 
00917         if ( !$wgDebugTimestamps ) {
00918                 return '';
00919         }
00920 
00921         $prefix = sprintf( "%6.4f", microtime( true ) - $wgRequestTime );
00922         $mem = sprintf( "%5.1fM", ( memory_get_usage( true ) / ( 1024 * 1024 ) ) );
00923         return "$prefix $mem  ";
00924 }
00925 
00931 function wfDebugMem( $exact = false ) {
00932         $mem = memory_get_usage();
00933         if( !$exact ) {
00934                 $mem = floor( $mem / 1024 ) . ' kilobytes';
00935         } else {
00936                 $mem .= ' bytes';
00937         }
00938         wfDebug( "Memory usage: $mem\n" );
00939 }
00940 
00950 function wfDebugLog( $logGroup, $text, $public = true ) {
00951         global $wgDebugLogGroups;
00952         $text = trim( $text ) . "\n";
00953         if( isset( $wgDebugLogGroups[$logGroup] ) ) {
00954                 $time = wfTimestamp( TS_DB );
00955                 $wiki = wfWikiID();
00956                 $host = wfHostname();
00957                 if ( wfRunHooks( 'Debug', array( $text, $logGroup ) ) ) {
00958                         wfErrorLog( "$time $host $wiki: $text", $wgDebugLogGroups[$logGroup] );
00959                 }
00960         } elseif ( $public === true ) {
00961                 wfDebug( $text, true );
00962         }
00963 }
00964 
00970 function wfLogDBError( $text ) {
00971         global $wgDBerrorLog;
00972         if ( $wgDBerrorLog ) {
00973                 $host = wfHostname();
00974                 $wiki = wfWikiID();
00975                 $text = date( 'D M j G:i:s T Y' ) . "\t$host\t$wiki\t$text";
00976                 wfErrorLog( $text, $wgDBerrorLog );
00977         }
00978 }
00979 
00989 function wfDeprecated( $function, $version = false, $component = false ) {
00990         static $functionsWarned = array();
00991 
00992         MWDebug::deprecated( $function, $version, $component );
00993 
00994         if ( !isset( $functionsWarned[$function] ) ) {
00995                 $functionsWarned[$function] = true;
00996                 
00997                 if ( $version ) {
00998                         global $wgDeprecationReleaseLimit;
00999                         
01000                         if ( $wgDeprecationReleaseLimit && $component === false ) {
01001                                 # Strip -* off the end of $version so that branches can use the
01002                                 # format #.##-branchname to avoid issues if the branch is merged into
01003                                 # a version of MediaWiki later than what it was branched from
01004                                 $comparableVersion = preg_replace( '/-.*$/', '', $version );
01005                                 
01006                                 # If the comparableVersion is larger than our release limit then
01007                                 # skip the warning message for the deprecation
01008                                 if ( version_compare( $wgDeprecationReleaseLimit, $comparableVersion, '<' ) ) {
01009                                         return;
01010                                 }
01011                         }
01012                         
01013                         $component = $component === false ? 'MediaWiki' : $component;
01014                         wfWarn( "Use of $function was deprecated in $component $version.", 2 );
01015                 } else {
01016                         wfWarn( "Use of $function is deprecated.", 2 );
01017                 }
01018         }
01019 }
01020 
01031 function wfWarn( $msg, $callerOffset = 1, $level = E_USER_NOTICE ) {
01032         global $wgDevelopmentWarnings;
01033 
01034         MWDebug::warning( $msg, $callerOffset + 2 );
01035 
01036         $callers = wfDebugBacktrace();
01037         if ( isset( $callers[$callerOffset + 1] ) ) {
01038                 $callerfunc = $callers[$callerOffset + 1];
01039                 $callerfile = $callers[$callerOffset];
01040                 if ( isset( $callerfile['file'] ) && isset( $callerfile['line'] ) ) {
01041                         $file = $callerfile['file'] . ' at line ' . $callerfile['line'];
01042                 } else {
01043                         $file = '(internal function)';
01044                 }
01045                 $func = '';
01046                 if ( isset( $callerfunc['class'] ) ) {
01047                         $func .= $callerfunc['class'] . '::';
01048                 }
01049                 if ( isset( $callerfunc['function'] ) ) {
01050                         $func .= $callerfunc['function'];
01051                 }
01052                 $msg .= " [Called from $func in $file]";
01053         }
01054 
01055         if ( $wgDevelopmentWarnings ) {
01056                 trigger_error( $msg, $level );
01057         } else {
01058                 wfDebug( "$msg\n" );
01059         }
01060 }
01061 
01071 function wfErrorLog( $text, $file ) {
01072         if ( substr( $file, 0, 4 ) == 'udp:' ) {
01073                 # Needs the sockets extension
01074                 if ( preg_match( '!^(tcp|udp):(?://)?\[([0-9a-fA-F:]+)\]:(\d+)(?:/(.*))?$!', $file, $m ) ) {
01075                         // IPv6 bracketed host
01076                         $host = $m[2];
01077                         $port = intval( $m[3] );
01078                         $prefix = isset( $m[4] ) ? $m[4] : false;
01079                         $domain = AF_INET6;
01080                 } elseif ( preg_match( '!^(tcp|udp):(?://)?([a-zA-Z0-9.-]+):(\d+)(?:/(.*))?$!', $file, $m ) ) {
01081                         $host = $m[2];
01082                         if ( !IP::isIPv4( $host ) ) {
01083                                 $host = gethostbyname( $host );
01084                         }
01085                         $port = intval( $m[3] );
01086                         $prefix = isset( $m[4] ) ? $m[4] : false;
01087                         $domain = AF_INET;
01088                 } else {
01089                         throw new MWException( __METHOD__ . ': Invalid UDP specification' );
01090                 }
01091 
01092                 // Clean it up for the multiplexer
01093                 if ( strval( $prefix ) !== '' ) {
01094                         $text = preg_replace( '/^/m', $prefix . ' ', $text );
01095 
01096                         // Limit to 64KB
01097                         if ( strlen( $text ) > 65506 ) {
01098                                 $text = substr( $text, 0, 65506 );
01099                         }
01100 
01101                         if ( substr( $text, -1 ) != "\n" ) {
01102                                 $text .= "\n";
01103                         }
01104                 } elseif ( strlen( $text ) > 65507 ) {
01105                         $text = substr( $text, 0, 65507 );
01106                 }
01107 
01108                 $sock = socket_create( $domain, SOCK_DGRAM, SOL_UDP );
01109                 if ( !$sock ) {
01110                         return;
01111                 }
01112 
01113                 socket_sendto( $sock, $text, strlen( $text ), 0, $host, $port );
01114                 socket_close( $sock );
01115         } else {
01116                 wfSuppressWarnings();
01117                 $exists = file_exists( $file );
01118                 $size = $exists ? filesize( $file ) : false;
01119                 if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
01120                         file_put_contents( $file, $text, FILE_APPEND );
01121                 }
01122                 wfRestoreWarnings();
01123         }
01124 }
01125 
01129 function wfLogProfilingData() {
01130         global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
01131         global $wgProfileLimit, $wgUser;
01132 
01133         $profiler = Profiler::instance();
01134 
01135         # Profiling must actually be enabled...
01136         if ( $profiler->isStub() ) {
01137                 return;
01138         }
01139 
01140         // Get total page request time and only show pages that longer than
01141         // $wgProfileLimit time (default is 0)
01142         $elapsed = microtime( true ) - $wgRequestTime;
01143         if ( $elapsed <= $wgProfileLimit ) {
01144                 return;
01145         }
01146 
01147         $profiler->logData();
01148 
01149         // Check whether this should be logged in the debug file.
01150         if ( $wgDebugLogFile == '' || ( !$wgDebugRawPage && wfIsDebugRawPage() ) ) {
01151                 return;
01152         }
01153 
01154         $forward = '';
01155         if ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
01156                 $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
01157         }
01158         if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
01159                 $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
01160         }
01161         if ( !empty( $_SERVER['HTTP_FROM'] ) ) {
01162                 $forward .= ' from ' . $_SERVER['HTTP_FROM'];
01163         }
01164         if ( $forward ) {
01165                 $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
01166         }
01167         // Don't load $wgUser at this late stage just for statistics purposes
01168         // @todo FIXME: We can detect some anons even if it is not loaded. See User::getId()
01169         if ( $wgUser->isItemLoaded( 'id' ) && $wgUser->isAnon() ) {
01170                 $forward .= ' anon';
01171         }
01172         $log = sprintf( "%s\t%04.3f\t%s\n",
01173                 gmdate( 'YmdHis' ), $elapsed,
01174                 urldecode( $wgRequest->getRequestURL() . $forward ) );
01175 
01176         wfErrorLog( $log . $profiler->getOutput(), $wgDebugLogFile );
01177 }
01178 
01186 function wfReadOnly() {
01187         global $wgReadOnlyFile, $wgReadOnly;
01188 
01189         if ( !is_null( $wgReadOnly ) ) {
01190                 return (bool)$wgReadOnly;
01191         }
01192         if ( $wgReadOnlyFile == '' ) {
01193                 return false;
01194         }
01195         // Set $wgReadOnly for faster access next time
01196         if ( is_file( $wgReadOnlyFile ) ) {
01197                 $wgReadOnly = file_get_contents( $wgReadOnlyFile );
01198         } else {
01199                 $wgReadOnly = false;
01200         }
01201         return (bool)$wgReadOnly;
01202 }
01203 
01207 function wfReadOnlyReason() {
01208         global $wgReadOnly;
01209         wfReadOnly();
01210         return $wgReadOnly;
01211 }
01212 
01228 function wfGetLangObj( $langcode = false ) {
01229         # Identify which language to get or create a language object for.
01230         # Using is_object here due to Stub objects.
01231         if( is_object( $langcode ) ) {
01232                 # Great, we already have the object (hopefully)!
01233                 return $langcode;
01234         }
01235 
01236         global $wgContLang, $wgLanguageCode;
01237         if( $langcode === true || $langcode === $wgLanguageCode ) {
01238                 # $langcode is the language code of the wikis content language object.
01239                 # or it is a boolean and value is true
01240                 return $wgContLang;
01241         }
01242 
01243         global $wgLang;
01244         if( $langcode === false || $langcode === $wgLang->getCode() ) {
01245                 # $langcode is the language code of user language object.
01246                 # or it was a boolean and value is false
01247                 return $wgLang;
01248         }
01249 
01250         $validCodes = array_keys( Language::getLanguageNames() );
01251         if( in_array( $langcode, $validCodes ) ) {
01252                 # $langcode corresponds to a valid language.
01253                 return Language::factory( $langcode );
01254         }
01255 
01256         # $langcode is a string, but not a valid language code; use content language.
01257         wfDebug( "Invalid language code passed to wfGetLangObj, falling back to content language.\n" );
01258         return $wgContLang;
01259 }
01260 
01268 function wfUILang() {
01269         wfDeprecated( __METHOD__, '1.18' );
01270         global $wgLang;
01271         return $wgLang;
01272 }
01273 
01283 function wfMessage( $key /*...*/) {
01284         $params = func_get_args();
01285         array_shift( $params );
01286         if ( isset( $params[0] ) && is_array( $params[0] ) ) {
01287                 $params = $params[0];
01288         }
01289         return new Message( $key, $params );
01290 }
01291 
01300 function wfMessageFallback( /*...*/ ) {
01301         $args = func_get_args();
01302         return MWFunction::callArray( 'Message::newFallbackSequence', $args );
01303 }
01304 
01322 function wfMsg( $key ) {
01323         $args = func_get_args();
01324         array_shift( $args );
01325         return wfMsgReal( $key, $args );
01326 }
01327 
01334 function wfMsgNoTrans( $key ) {
01335         $args = func_get_args();
01336         array_shift( $args );
01337         return wfMsgReal( $key, $args, true, false, false );
01338 }
01339 
01363 function wfMsgForContent( $key ) {
01364         global $wgForceUIMsgAsContentMsg;
01365         $args = func_get_args();
01366         array_shift( $args );
01367         $forcontent = true;
01368         if( is_array( $wgForceUIMsgAsContentMsg ) &&
01369                 in_array( $key, $wgForceUIMsgAsContentMsg ) )
01370         {
01371                 $forcontent = false;
01372         }
01373         return wfMsgReal( $key, $args, true, $forcontent );
01374 }
01375 
01382 function wfMsgForContentNoTrans( $key ) {
01383         global $wgForceUIMsgAsContentMsg;
01384         $args = func_get_args();
01385         array_shift( $args );
01386         $forcontent = true;
01387         if( is_array( $wgForceUIMsgAsContentMsg ) &&
01388                 in_array( $key, $wgForceUIMsgAsContentMsg ) )
01389         {
01390                 $forcontent = false;
01391         }
01392         return wfMsgReal( $key, $args, true, $forcontent, false );
01393 }
01394 
01405 function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
01406         wfProfileIn( __METHOD__ );
01407         $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
01408         $message = wfMsgReplaceArgs( $message, $args );
01409         wfProfileOut( __METHOD__ );
01410         return $message;
01411 }
01412 
01423 function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
01424         wfRunHooks( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
01425 
01426         $cache = MessageCache::singleton();
01427         $message = $cache->get( $key, $useDB, $langCode );
01428         if( $message === false ) {
01429                 $message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
01430         } elseif ( $transform ) {
01431                 $message = $cache->transform( $message );
01432         }
01433         return $message;
01434 }
01435 
01444 function wfMsgReplaceArgs( $message, $args ) {
01445         # Fix windows line-endings
01446         # Some messages are split with explode("\n", $msg)
01447         $message = str_replace( "\r", '', $message );
01448 
01449         // Replace arguments
01450         if ( count( $args ) ) {
01451                 if ( is_array( $args[0] ) ) {
01452                         $args = array_values( $args[0] );
01453                 }
01454                 $replacementKeys = array();
01455                 foreach( $args as $n => $param ) {
01456                         $replacementKeys['$' . ( $n + 1 )] = $param;
01457                 }
01458                 $message = strtr( $message, $replacementKeys );
01459         }
01460 
01461         return $message;
01462 }
01463 
01475 function wfMsgHtml( $key ) {
01476         $args = func_get_args();
01477         array_shift( $args );
01478         return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args );
01479 }
01480 
01492 function wfMsgWikiHtml( $key ) {
01493         $args = func_get_args();
01494         array_shift( $args );
01495         return wfMsgReplaceArgs(
01496                 MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
01497                 /* can't be set to false */ true, /* interface */ true )->getText(),
01498                 $args );
01499 }
01500 
01520 function wfMsgExt( $key, $options ) {
01521         $args = func_get_args();
01522         array_shift( $args );
01523         array_shift( $args );
01524         $options = (array)$options;
01525 
01526         foreach( $options as $arrayKey => $option ) {
01527                 if( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
01528                         # An unknown index, neither numeric nor "language"
01529                         wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
01530                 } elseif( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option,
01531                 array( 'parse', 'parseinline', 'escape', 'escapenoentities',
01532                 'replaceafter', 'parsemag', 'content' ) ) ) {
01533                         # A numeric index with unknown value
01534                         wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
01535                 }
01536         }
01537 
01538         if( in_array( 'content', $options, true ) ) {
01539                 $forContent = true;
01540                 $langCode = true;
01541                 $langCodeObj = null;
01542         } elseif( array_key_exists( 'language', $options ) ) {
01543                 $forContent = false;
01544                 $langCode = wfGetLangObj( $options['language'] );
01545                 $langCodeObj = $langCode;
01546         } else {
01547                 $forContent = false;
01548                 $langCode = false;
01549                 $langCodeObj = null;
01550         }
01551 
01552         $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
01553 
01554         if( !in_array( 'replaceafter', $options, true ) ) {
01555                 $string = wfMsgReplaceArgs( $string, $args );
01556         }
01557 
01558         $messageCache = MessageCache::singleton();
01559         $parseInline = in_array( 'parseinline', $options, true );
01560         if( in_array( 'parse', $options, true ) || $parseInline ) {
01561                 $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
01562                 if ( $string instanceof ParserOutput ) {
01563                         $string = $string->getText();
01564                 }
01565 
01566                 if ( $parseInline ) {
01567                         $m = array();
01568                         if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
01569                                 $string = $m[1];
01570                         }
01571                 }
01572         } elseif ( in_array( 'parsemag', $options, true ) ) {
01573                 $string = $messageCache->transform( $string,
01574                                 !$forContent, $langCodeObj );
01575         }
01576 
01577         if ( in_array( 'escape', $options, true ) ) {
01578                 $string = htmlspecialchars ( $string );
01579         } elseif ( in_array( 'escapenoentities', $options, true ) ) {
01580                 $string = Sanitizer::escapeHtmlAllowEntities( $string );
01581         }
01582 
01583         if( in_array( 'replaceafter', $options, true ) ) {
01584                 $string = wfMsgReplaceArgs( $string, $args );
01585         }
01586 
01587         return $string;
01588 }
01589 
01598 function wfEmptyMsg( $key ) {
01599         return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false;
01600 }
01601 
01608 function wfDebugDieBacktrace( $msg = '' ) {
01609         throw new MWException( $msg );
01610 }
01611 
01619 function wfHostname() {
01620         static $host;
01621         if ( is_null( $host ) ) {
01622                 if ( function_exists( 'posix_uname' ) ) {
01623                         // This function not present on Windows
01624                         $uname = posix_uname();
01625                 } else {
01626                         $uname = false;
01627                 }
01628                 if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
01629                         $host = $uname['nodename'];
01630                 } elseif ( getenv( 'COMPUTERNAME' ) ) {
01631                         # Windows computer name
01632                         $host = getenv( 'COMPUTERNAME' );
01633                 } else {
01634                         # This may be a virtual server.
01635                         $host = $_SERVER['SERVER_NAME'];
01636                 }
01637         }
01638         return $host;
01639 }
01640 
01647 function wfReportTime() {
01648         global $wgRequestTime, $wgShowHostnames;
01649 
01650         $elapsed = microtime( true ) - $wgRequestTime;
01651 
01652         return $wgShowHostnames
01653                 ? sprintf( '<!-- Served by %s in %01.3f secs. -->', wfHostname(), $elapsed )
01654                 : sprintf( '<!-- Served in %01.3f secs. -->', $elapsed );
01655 }
01656 
01672 function wfDebugBacktrace( $limit = 0 ) {
01673         static $disabled = null;
01674 
01675         if( extension_loaded( 'Zend Optimizer' ) ) {
01676                 wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
01677                 return array();
01678         }
01679 
01680         if ( is_null( $disabled ) ) {
01681                 $disabled = false;
01682                 $functions = explode( ',', ini_get( 'disable_functions' ) );
01683                 $functions = array_map( 'trim', $functions );
01684                 $functions = array_map( 'strtolower', $functions );
01685                 if ( in_array( 'debug_backtrace', $functions ) ) {
01686                         wfDebug( "debug_backtrace is in disabled_functions\n" );
01687                         $disabled = true;
01688                 }
01689         }
01690         if ( $disabled ) {
01691                 return array();
01692         }
01693 
01694         if ( $limit && version_compare( PHP_VERSION, '5.4.0', '>=' ) ) {
01695                 return array_slice( debug_backtrace( DEBUG_BACKTRACE_PROVIDE_OBJECT, $limit ), 1 );
01696         } else {
01697                 return array_slice( debug_backtrace(), 1 );
01698         }
01699 }
01700 
01706 function wfBacktrace() {
01707         global $wgCommandLineMode;
01708 
01709         if ( $wgCommandLineMode ) {
01710                 $msg = '';
01711         } else {
01712                 $msg = "<ul>\n";
01713         }
01714         $backtrace = wfDebugBacktrace();
01715         foreach( $backtrace as $call ) {
01716                 if( isset( $call['file'] ) ) {
01717                         $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
01718                         $file = $f[count( $f ) - 1];
01719                 } else {
01720                         $file = '-';
01721                 }
01722                 if( isset( $call['line'] ) ) {
01723                         $line = $call['line'];
01724                 } else {
01725                         $line = '-';
01726                 }
01727                 if ( $wgCommandLineMode ) {
01728                         $msg .= "$file line $line calls ";
01729                 } else {
01730                         $msg .= '<li>' . $file . ' line ' . $line . ' calls ';
01731                 }
01732                 if( !empty( $call['class'] ) ) {
01733                         $msg .= $call['class'] . $call['type'];
01734                 }
01735                 $msg .= $call['function'] . '()';
01736 
01737                 if ( $wgCommandLineMode ) {
01738                         $msg .= "\n";
01739                 } else {
01740                         $msg .= "</li>\n";
01741                 }
01742         }
01743         if ( $wgCommandLineMode ) {
01744                 $msg .= "\n";
01745         } else {
01746                 $msg .= "</ul>\n";
01747         }
01748 
01749         return $msg;
01750 }
01751 
01758 function wfGetCaller( $level = 2 ) {
01759         $backtrace = wfDebugBacktrace( $level );
01760         if ( isset( $backtrace[$level] ) ) {
01761                 return wfFormatStackFrame( $backtrace[$level] );
01762         } else {
01763                 $caller = 'unknown';
01764         }
01765         return $caller;
01766 }
01767 
01776 function wfGetAllCallers( $limit = 3 ) {
01777         $trace = array_reverse( wfDebugBacktrace() );
01778         if ( !$limit || $limit > count( $trace ) - 1 ) {
01779                 $limit = count( $trace ) - 1;
01780         }
01781         $trace = array_slice( $trace, -$limit - 1, $limit );
01782         return implode( '/', array_map( 'wfFormatStackFrame', $trace ) );
01783 }
01784 
01791 function wfFormatStackFrame( $frame ) {
01792         return isset( $frame['class'] ) ?
01793                 $frame['class'] . '::' . $frame['function'] :
01794                 $frame['function'];
01795 }
01796 
01797 
01798 /* Some generic result counters, pulled out of SearchEngine */
01799 
01800 
01808 function wfShowingResults( $offset, $limit ) {
01809         global $wgLang;
01810         return wfMsgExt(
01811                 'showingresults',
01812                 array( 'parseinline' ),
01813                 $wgLang->formatNum( $limit ),
01814                 $wgLang->formatNum( $offset + 1 )
01815         );
01816 }
01817 
01829 function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
01830         wfDeprecated( __METHOD__, '1.19' );
01831         
01832         global $wgLang;
01833 
01834         $query = wfCgiToArray( $query );
01835 
01836         if( is_object( $link ) ) {
01837                 $title = $link;
01838         } else {
01839                 $title = Title::newFromText( $link );
01840                 if( is_null( $title ) ) {
01841                         return false;
01842                 }
01843         }
01844 
01845         return $wgLang->viewPrevNext( $title, $offset, $limit, $query, $atend );
01846 }
01847 
01858 function wfSpecialList( $page, $details, $oppositedm = true ) {
01859         global $wgLang;
01860         return $wgLang->specialList( $page, $details, $oppositedm );
01861 }
01862 
01870 function wfClientAcceptsGzip( $force = false ) {
01871         static $result = null;
01872         if ( $result === null || $force ) {
01873                 $result = false;
01874                 if( isset( $_SERVER['HTTP_ACCEPT_ENCODING'] ) ) {
01875                         # @todo FIXME: We may want to blacklist some broken browsers
01876                         $m = array();
01877                         if( preg_match(
01878                                 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
01879                                 $_SERVER['HTTP_ACCEPT_ENCODING'],
01880                                 $m )
01881                         )
01882                         {
01883                                 if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) {
01884                                         $result = false;
01885                                         return $result;
01886                                 }
01887                                 wfDebug( "wfClientAcceptsGzip: client accepts gzip.\n" );
01888                                 $result = true;
01889                         }
01890                 }
01891         }
01892         return $result;
01893 }
01894 
01904 function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
01905         global $wgRequest;
01906         return $wgRequest->getLimitOffset( $deflimit, $optionname );
01907 }
01908 
01918 function wfEscapeWikiText( $text ) {
01919         $text = strtr( "\n$text", array(
01920                 '"' => '&#34;', '&' => '&#38;', "'" => '&#39;', '<' => '&#60;',
01921                 '=' => '&#61;', '>' => '&#62;', '[' => '&#91;', ']' => '&#93;',
01922                 '{' => '&#123;', '|' => '&#124;', '}' => '&#125;',
01923                 "\n#" => "\n&#35;", "\n*" => "\n&#42;",
01924                 "\n:" => "\n&#58;", "\n;" => "\n&#59;",
01925                 '://' => '&#58;//', 'ISBN ' => 'ISBN&#32;', 'RFC ' => 'RFC&#32;',
01926         ) );
01927         return substr( $text, 1 );
01928 }
01929 
01934 function wfTime() {
01935         return microtime( true );
01936 }
01937 
01948 function wfSetVar( &$dest, $source, $force = false ) {
01949         $temp = $dest;
01950         if ( !is_null( $source ) || $force ) {
01951                 $dest = $source;
01952         }
01953         return $temp;
01954 }
01955 
01965 function wfSetBit( &$dest, $bit, $state = true ) {
01966         $temp = (bool)( $dest & $bit );
01967         if ( !is_null( $state ) ) {
01968                 if ( $state ) {
01969                         $dest |= $bit;
01970                 } else {
01971                         $dest &= ~$bit;
01972                 }
01973         }
01974         return $temp;
01975 }
01976 
01983 function wfVarDump( $var ) {
01984         global $wgOut;
01985         $s = str_replace( "\n", "<br />\n", var_export( $var, true ) . "\n" );
01986         if ( headers_sent() || !isset( $wgOut ) || !is_object( $wgOut ) ) {
01987                 print $s;
01988         } else {
01989                 $wgOut->addHTML( $s );
01990         }
01991 }
01992 
02000 function wfHttpError( $code, $label, $desc ) {
02001         global $wgOut;
02002         $wgOut->disable();
02003         header( "HTTP/1.0 $code $label" );
02004         header( "Status: $code $label" );
02005         $wgOut->sendCacheControl();
02006 
02007         header( 'Content-type: text/html; charset=utf-8' );
02008         print "<!doctype html>" .
02009                 '<html><head><title>' .
02010                 htmlspecialchars( $label ) .
02011                 '</title></head><body><h1>' .
02012                 htmlspecialchars( $label ) .
02013                 '</h1><p>' .
02014                 nl2br( htmlspecialchars( $desc ) ) .
02015                 "</p></body></html>\n";
02016 }
02017 
02035 function wfResetOutputBuffers( $resetGzipEncoding = true ) {
02036         if( $resetGzipEncoding ) {
02037                 // Suppress Content-Encoding and Content-Length
02038                 // headers from 1.10+s wfOutputHandler
02039                 global $wgDisableOutputCompression;
02040                 $wgDisableOutputCompression = true;
02041         }
02042         while( $status = ob_get_status() ) {
02043                 if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
02044                         // Probably from zlib.output_compression or other
02045                         // PHP-internal setting which can't be removed.
02046                         //
02047                         // Give up, and hope the result doesn't break
02048                         // output behavior.
02049                         break;
02050                 }
02051                 if( !ob_end_clean() ) {
02052                         // Could not remove output buffer handler; abort now
02053                         // to avoid getting in some kind of infinite loop.
02054                         break;
02055                 }
02056                 if( $resetGzipEncoding ) {
02057                         if( $status['name'] == 'ob_gzhandler' ) {
02058                                 // Reset the 'Content-Encoding' field set by this handler
02059                                 // so we can start fresh.
02060                                 if ( function_exists( 'header_remove' ) ) {
02061                                         // Available since PHP 5.3.0
02062                                         header_remove( 'Content-Encoding' );
02063                                 } else {
02064                                         // We need to provide a valid content-coding. See bug 28069
02065                                         header( 'Content-Encoding: identity' );
02066                                 }
02067                                 break;
02068                         }
02069                 }
02070         }
02071 }
02072 
02085 function wfClearOutputBuffers() {
02086         wfResetOutputBuffers( false );
02087 }
02088 
02097 function wfAcceptToPrefs( $accept, $def = '*/*' ) {
02098         # No arg means accept anything (per HTTP spec)
02099         if( !$accept ) {
02100                 return array( $def => 1.0 );
02101         }
02102 
02103         $prefs = array();
02104 
02105         $parts = explode( ',', $accept );
02106 
02107         foreach( $parts as $part ) {
02108                 # @todo FIXME: Doesn't deal with params like 'text/html; level=1'
02109                 $values = explode( ';', trim( $part ) );
02110                 $match = array();
02111                 if ( count( $values ) == 1 ) {
02112                         $prefs[$values[0]] = 1.0;
02113                 } elseif ( preg_match( '/q\s*=\s*(\d*\.\d+)/', $values[1], $match ) ) {
02114                         $prefs[$values[0]] = floatval( $match[1] );
02115                 }
02116         }
02117 
02118         return $prefs;
02119 }
02120 
02133 function mimeTypeMatch( $type, $avail ) {
02134         if( array_key_exists( $type, $avail ) ) {
02135                 return $type;
02136         } else {
02137                 $parts = explode( '/', $type );
02138                 if( array_key_exists( $parts[0] . '/*', $avail ) ) {
02139                         return $parts[0] . '/*';
02140                 } elseif( array_key_exists( '*/*', $avail ) ) {
02141                         return '*/*';
02142                 } else {
02143                         return null;
02144                 }
02145         }
02146 }
02147 
02161 function wfNegotiateType( $cprefs, $sprefs ) {
02162         $combine = array();
02163 
02164         foreach( array_keys( $sprefs ) as $type ) {
02165                 $parts = explode( '/', $type );
02166                 if( $parts[1] != '*' ) {
02167                         $ckey = mimeTypeMatch( $type, $cprefs );
02168                         if( $ckey ) {
02169                                 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
02170                         }
02171                 }
02172         }
02173 
02174         foreach( array_keys( $cprefs ) as $type ) {
02175                 $parts = explode( '/', $type );
02176                 if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
02177                         $skey = mimeTypeMatch( $type, $sprefs );
02178                         if( $skey ) {
02179                                 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
02180                         }
02181                 }
02182         }
02183 
02184         $bestq = 0;
02185         $besttype = null;
02186 
02187         foreach( array_keys( $combine ) as $type ) {
02188                 if( $combine[$type] > $bestq ) {
02189                         $besttype = $type;
02190                         $bestq = $combine[$type];
02191                 }
02192         }
02193 
02194         return $besttype;
02195 }
02196 
02202 function wfSuppressWarnings( $end = false ) {
02203         static $suppressCount = 0;
02204         static $originalLevel = false;
02205 
02206         if ( $end ) {
02207                 if ( $suppressCount ) {
02208                         --$suppressCount;
02209                         if ( !$suppressCount ) {
02210                                 error_reporting( $originalLevel );
02211                         }
02212                 }
02213         } else {
02214                 if ( !$suppressCount ) {
02215                         // E_DEPRECATED and E_USER_DEPRECATED are undefined in PHP 5.2
02216                         if( !defined( 'E_DEPRECATED' ) ) {
02217                                 define( 'E_DEPRECATED', 8192 );
02218                         }
02219                         if( !defined( 'E_USER_DEPRECATED' ) ) {
02220                                 define( 'E_USER_DEPRECATED', 16384 );
02221                         }
02222                         $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_DEPRECATED | E_USER_DEPRECATED | E_STRICT ) );
02223                 }
02224                 ++$suppressCount;
02225         }
02226 }
02227 
02231 function wfRestoreWarnings() {
02232         wfSuppressWarnings( true );
02233 }
02234 
02235 # Autodetect, convert and provide timestamps of various types
02236 
02240 define( 'TS_UNIX', 0 );
02241 
02245 define( 'TS_MW', 1 );
02246 
02250 define( 'TS_DB', 2 );
02251 
02255 define( 'TS_RFC2822', 3 );
02256 
02262 define( 'TS_ISO_8601', 4 );
02263 
02271 define( 'TS_EXIF', 5 );
02272 
02276 define( 'TS_ORACLE', 6 );
02277 
02281 define( 'TS_POSTGRES', 7 );
02282 
02286 define( 'TS_DB2', 8 );
02287 
02291 define( 'TS_ISO_8601_BASIC', 9 );
02292 
02302 function wfTimestamp( $outputtype = TS_UNIX, $ts = 0 ) {
02303         $uts = 0;
02304         $da = array();
02305         $strtime = '';
02306 
02307         if ( !$ts ) { // We want to catch 0, '', null... but not date strings starting with a letter.
02308                 $uts = time();
02309                 $strtime = "@$uts";
02310         } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
02311                 # TS_DB
02312         } elseif ( preg_match( '/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D', $ts, $da ) ) {
02313                 # TS_EXIF
02314         } elseif ( preg_match( '/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D', $ts, $da ) ) {
02315                 # TS_MW
02316         } elseif ( preg_match( '/^-?\d{1,13}$/D', $ts ) ) {
02317                 # TS_UNIX
02318                 $uts = $ts;
02319                 $strtime = "@$ts"; // http://php.net/manual/en/datetime.formats.compound.php
02320         } elseif ( preg_match( '/^\d{2}-\d{2}-\d{4} \d{2}:\d{2}:\d{2}.\d{6}$/', $ts ) ) {
02321                 # TS_ORACLE // session altered to DD-MM-YYYY HH24:MI:SS.FF6
02322                 $strtime = preg_replace( '/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
02323                                 str_replace( '+00:00', 'UTC', $ts ) );
02324         } elseif ( preg_match( '/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
02325                 # TS_ISO_8601
02326         } elseif ( preg_match( '/^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})(?:\.*\d*)?Z$/', $ts, $da ) ) {
02327                 #TS_ISO_8601_BASIC
02328         } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d*[\+\- ](\d\d)$/', $ts, $da ) ) {
02329                 # TS_POSTGRES
02330         } elseif ( preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.*\d* GMT$/', $ts, $da ) ) {
02331                 # TS_POSTGRES
02332         } elseif (preg_match( '/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)\.\d\d\d$/', $ts, $da ) ) {
02333                 # TS_DB2
02334         } elseif ( preg_match( '/^[ \t\r\n]*([A-Z][a-z]{2},[ \t\r\n]*)?' . # Day of week
02335                                                         '\d\d?[ \t\r\n]*[A-Z][a-z]{2}[ \t\r\n]*\d{2}(?:\d{2})?' .  # dd Mon yyyy
02336                                                         '[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d[ \t\r\n]*:[ \t\r\n]*\d\d/S', $ts ) ) { # hh:mm:ss
02337                 # TS_RFC2822, accepting a trailing comment. See http://www.squid-cache.org/mail-archive/squid-users/200307/0122.html / r77171
02338                 # The regex is a superset of rfc2822 for readability
02339                 $strtime = strtok( $ts, ';' );
02340         } elseif ( preg_match( '/^[A-Z][a-z]{5,8}, \d\d-[A-Z][a-z]{2}-\d{2} \d\d:\d\d:\d\d/', $ts ) ) {
02341                 # TS_RFC850
02342                 $strtime = $ts;
02343         } elseif ( preg_match( '/^[A-Z][a-z]{2} [A-Z][a-z]{2} +\d{1,2} \d\d:\d\d:\d\d \d{4}/', $ts ) ) {
02344                 # asctime
02345                 $strtime = $ts;
02346         } else {
02347                 # Bogus value...
02348                 wfDebug("wfTimestamp() fed bogus time value: TYPE=$outputtype; VALUE=$ts\n");
02349 
02350                 return false;
02351         }
02352 
02353         static $formats = array(
02354                 TS_UNIX => 'U',
02355                 TS_MW => 'YmdHis',
02356                 TS_DB => 'Y-m-d H:i:s',
02357                 TS_ISO_8601 => 'Y-m-d\TH:i:s\Z',
02358                 TS_ISO_8601_BASIC => 'Ymd\THis\Z',
02359                 TS_EXIF => 'Y:m:d H:i:s', // This shouldn't ever be used, but is included for completeness
02360                 TS_RFC2822 => 'D, d M Y H:i:s',
02361                 TS_ORACLE => 'd-m-Y H:i:s.000000', // Was 'd-M-y h.i.s A' . ' +00:00' before r51500
02362                 TS_POSTGRES => 'Y-m-d H:i:s',
02363                 TS_DB2 => 'Y-m-d H:i:s',
02364         );
02365 
02366         if ( !isset( $formats[$outputtype] ) ) {
02367                 throw new MWException( 'wfTimestamp() called with illegal output type.' );
02368         }
02369 
02370         if ( function_exists( "date_create" ) ) {
02371                 if ( count( $da ) ) {
02372                         $ds = sprintf("%04d-%02d-%02dT%02d:%02d:%02d.00+00:00",
02373                                 (int)$da[1], (int)$da[2], (int)$da[3],
02374                                 (int)$da[4], (int)$da[5], (int)$da[6]);
02375 
02376                         $d = date_create( $ds, new DateTimeZone( 'GMT' ) );
02377                 } elseif ( $strtime ) {
02378                         $d = date_create( $strtime, new DateTimeZone( 'GMT' ) );
02379                 } else {
02380                         return false;
02381                 }
02382 
02383                 if ( !$d ) {
02384                         wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
02385                         return false;
02386                 }
02387 
02388                 $output = $d->format( $formats[$outputtype] );
02389         } else {
02390                 if ( count( $da ) ) {
02391                         // Warning! gmmktime() acts oddly if the month or day is set to 0
02392                         // We may want to handle that explicitly at some point
02393                         $uts = gmmktime( (int)$da[4], (int)$da[5], (int)$da[6],
02394                                 (int)$da[2], (int)$da[3], (int)$da[1] );
02395                 } elseif ( $strtime ) {
02396                         $uts = strtotime( $strtime );
02397                 }
02398 
02399                 if ( $uts === false ) {
02400                         wfDebug("wfTimestamp() can't parse the timestamp (non 32-bit time? Update php): $outputtype; $ts\n");
02401                         return false;
02402                 }
02403 
02404                 if ( TS_UNIX == $outputtype ) {
02405                         return $uts;
02406                 }
02407                 $output = gmdate( $formats[$outputtype], $uts );
02408         }
02409 
02410         if ( ( $outputtype == TS_RFC2822 ) || ( $outputtype == TS_POSTGRES ) ) {
02411                 $output .= ' GMT';
02412         }
02413 
02414         return $output;
02415 }
02416 
02425 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
02426         if( is_null( $ts ) ) {
02427                 return null;
02428         } else {
02429                 return wfTimestamp( $outputtype, $ts );
02430         }
02431 }
02432 
02438 function wfTimestampNow() {
02439         # return NOW
02440         return wfTimestamp( TS_MW, time() );
02441 }
02442 
02448 function wfIsWindows() {
02449         static $isWindows = null;
02450         if ( $isWindows === null ) {
02451                 $isWindows = substr( php_uname(), 0, 7 ) == 'Windows';
02452         }
02453         return $isWindows;
02454 }
02455 
02461 function wfIsHipHop() {
02462         return function_exists( 'hphp_thread_set_warmup_enabled' );
02463 }
02464 
02471 function swap( &$x, &$y ) {
02472         $z = $x;
02473         $x = $y;
02474         $y = $z;
02475 }
02476 
02489 function wfTempDir() {
02490         foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) {
02491                 $tmp = getenv( $var );
02492                 if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
02493                         return $tmp;
02494                 }
02495         }
02496         if( function_exists( 'sys_get_temp_dir' ) ) {
02497                 return sys_get_temp_dir();
02498         }
02499         # Usual defaults
02500         return wfIsWindows() ? 'C:\Windows\Temp' : '/tmp';
02501 }
02502 
02511 function wfMkdirParents( $dir, $mode = null, $caller = null ) {
02512         global $wgDirectoryMode;
02513 
02514         if ( FileBackend::isStoragePath( $dir ) ) { // sanity
02515                 throw new MWException( __FUNCTION__ . " given storage path `$dir`.");
02516         }
02517 
02518         if ( !is_null( $caller ) ) {
02519                 wfDebug( "$caller: called wfMkdirParents($dir)\n" );
02520         }
02521 
02522         if( strval( $dir ) === '' || file_exists( $dir ) ) {
02523                 return true;
02524         }
02525 
02526         $dir = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $dir );
02527 
02528         if ( is_null( $mode ) ) {
02529                 $mode = $wgDirectoryMode;
02530         }
02531 
02532         // Turn off the normal warning, we're doing our own below
02533         wfSuppressWarnings();
02534         $ok = mkdir( $dir, $mode, true ); // PHP5 <3
02535         wfRestoreWarnings();
02536 
02537         if( !$ok ) {
02538                 // PHP doesn't report the path in its warning message, so add our own to aid in diagnosis.
02539                 trigger_error( __FUNCTION__ . ": failed to mkdir \"$dir\" mode $mode", E_USER_WARNING );
02540         }
02541         return $ok;
02542 }
02543 
02550 function wfIncrStats( $key, $count = 1 ) {
02551         global $wgStatsMethod;
02552 
02553         $count = intval( $count );
02554 
02555         if( $wgStatsMethod == 'udp' ) {
02556                 global $wgUDPProfilerHost, $wgUDPProfilerPort, $wgDBname, $wgAggregateStatsID;
02557                 static $socket;
02558 
02559                 $id = $wgAggregateStatsID !== false ? $wgAggregateStatsID : $wgDBname;
02560 
02561                 if ( !$socket ) {
02562                         $socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
02563                         $statline = "stats/{$id} - {$count} 1 1 1 1 -total\n";
02564                         socket_sendto(
02565                                 $socket,
02566                                 $statline,
02567                                 strlen( $statline ),
02568                                 0,
02569                                 $wgUDPProfilerHost,
02570                                 $wgUDPProfilerPort
02571                         );
02572                 }
02573                 $statline = "stats/{$id} - {$count} 1 1 1 1 {$key}\n";
02574                 wfSuppressWarnings();
02575                 socket_sendto(
02576                         $socket,
02577                         $statline,
02578                         strlen( $statline ),
02579                         0,
02580                         $wgUDPProfilerHost,
02581                         $wgUDPProfilerPort
02582                 );
02583                 wfRestoreWarnings();
02584         } elseif( $wgStatsMethod == 'cache' ) {
02585                 global $wgMemc;
02586                 $key = wfMemcKey( 'stats', $key );
02587                 if ( is_null( $wgMemc->incr( $key, $count ) ) ) {
02588                         $wgMemc->add( $key, $count );
02589                 }
02590         } else {
02591                 // Disabled
02592         }
02593 }
02594 
02599 function wfRecursiveRemoveDir( $dir ) {
02600         wfDebug( __FUNCTION__ . "( $dir )\n" );
02601         // taken from http://de3.php.net/manual/en/function.rmdir.php#98622
02602         if ( is_dir( $dir ) ) {
02603                 $objects = scandir( $dir );
02604                 foreach ( $objects as $object ) {
02605                         if ( $object != "." && $object != ".." ) {
02606                                 if ( filetype( $dir . '/' . $object ) == "dir" ) {
02607                                         wfRecursiveRemoveDir( $dir . '/' . $object );
02608                                 } else {
02609                                         unlink( $dir . '/' . $object );
02610                                 }
02611                         }
02612                 }
02613                 reset( $objects );
02614                 rmdir( $dir );
02615         }
02616 }
02617 
02624 function wfPercent( $nr, $acc = 2, $round = true ) {
02625         $ret = sprintf( "%.${acc}f", $nr );
02626         return $round ? round( $ret, $acc ) . '%' : "$ret%";
02627 }
02628 
02637 function in_string( $needle, $str, $insensitive = false ) {
02638         $func = 'strpos';
02639         if( $insensitive ) $func = 'stripos';
02640 
02641         return $func( $str, $needle ) !== false;
02642 }
02643 
02667 function wfIniGetBool( $setting ) {
02668         $val = ini_get( $setting );
02669         // 'on' and 'true' can't have whitespace around them, but '1' can.
02670         return strtolower( $val ) == 'on'
02671                 || strtolower( $val ) == 'true'
02672                 || strtolower( $val ) == 'yes'
02673                 || preg_match( "/^\s*[+-]?0*[1-9]/", $val ); // approx C atoi() function
02674 }
02675 
02685 function wfDl( $extension, $fileName = null ) {
02686         if( extension_loaded( $extension ) ) {
02687                 return true;
02688         }
02689 
02690         $canDl = false;
02691         $sapi = php_sapi_name();
02692         if( version_compare( PHP_VERSION, '5.3.0', '<' ) ||
02693                 $sapi == 'cli' || $sapi == 'cgi' || $sapi == 'embed' )
02694         {
02695                 $canDl = ( function_exists( 'dl' ) && is_callable( 'dl' )
02696                 && wfIniGetBool( 'enable_dl' ) && !wfIniGetBool( 'safe_mode' ) );
02697         }
02698 
02699         if( $canDl ) {
02700                 $fileName = $fileName ? $fileName : $extension;
02701                 if( wfIsWindows() ) {
02702                         $fileName = 'php_' . $fileName;
02703                 }
02704                 wfSuppressWarnings();
02705                 dl( $fileName . '.' . PHP_SHLIB_SUFFIX );
02706                 wfRestoreWarnings();
02707         }
02708         return extension_loaded( $extension );
02709 }
02710 
02722 function wfEscapeShellArg( ) {
02723         wfInitShellLocale();
02724 
02725         $args = func_get_args();
02726         $first = true;
02727         $retVal = '';
02728         foreach ( $args as $arg ) {
02729                 if ( !$first ) {
02730                         $retVal .= ' ';
02731                 } else {
02732                         $first = false;
02733                 }
02734 
02735                 if ( wfIsWindows() ) {
02736                         // Escaping for an MSVC-style command line parser and CMD.EXE
02737                         // Refs:
02738                         //  * http://web.archive.org/web/20020708081031/http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
02739                         //  * http://technet.microsoft.com/en-us/library/cc723564.aspx
02740                         //  * Bug #13518
02741                         //  * CR r63214
02742                         // Double the backslashes before any double quotes. Escape the double quotes.
02743                         $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
02744                         $arg = '';
02745                         $iteration = 0;
02746                         foreach ( $tokens as $token ) {
02747                                 if ( $iteration % 2 == 1 ) {
02748                                         // Delimiter, a double quote preceded by zero or more slashes
02749                                         $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
02750                                 } elseif ( $iteration % 4 == 2 ) {
02751                                         // ^ in $token will be outside quotes, need to be escaped
02752                                         $arg .= str_replace( '^', '^^', $token );
02753                                 } else { // $iteration % 4 == 0
02754                                         // ^ in $token will appear inside double quotes, so leave as is
02755                                         $arg .= $token;
02756                                 }
02757                                 $iteration++;
02758                         }
02759                         // Double the backslashes before the end of the string, because
02760                         // we will soon add a quote
02761                         $m = array();
02762                         if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
02763                                 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
02764                         }
02765 
02766                         // Add surrounding quotes
02767                         $retVal .= '"' . $arg . '"';
02768                 } else {
02769                         $retVal .= escapeshellarg( $arg );
02770                 }
02771         }
02772         return $retVal;
02773 }
02774 
02785 function wfShellExec( $cmd, &$retval = null, $environ = array() ) {
02786         global $IP, $wgMaxShellMemory, $wgMaxShellFileSize, $wgMaxShellTime;
02787 
02788         static $disabled;
02789         if ( is_null( $disabled ) ) {
02790                 $disabled = false;
02791                 if( wfIniGetBool( 'safe_mode' ) ) {
02792                         wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
02793                         $disabled = 'safemode';
02794                 } else {
02795                         $functions = explode( ',', ini_get( 'disable_functions' ) );
02796                         $functions = array_map( 'trim', $functions );
02797                         $functions = array_map( 'strtolower', $functions );
02798                         if ( in_array( 'passthru', $functions ) ) {
02799                                 wfDebug( "passthru is in disabled_functions\n" );
02800                                 $disabled = 'passthru';
02801                         }
02802                 }
02803         }
02804         if ( $disabled ) {
02805                 $retval = 1;
02806                 return $disabled == 'safemode' ?
02807                         'Unable to run external programs in safe mode.' :
02808                         'Unable to run external programs, passthru() is disabled.';
02809         }
02810 
02811         wfInitShellLocale();
02812 
02813         $envcmd = '';
02814         foreach( $environ as $k => $v ) {
02815                 if ( wfIsWindows() ) {
02816                         /* Surrounding a set in quotes (method used by wfEscapeShellArg) makes the quotes themselves
02817                          * appear in the environment variable, so we must use carat escaping as documented in
02818                          * http://technet.microsoft.com/en-us/library/cc723564.aspx
02819                          * Note however that the quote isn't listed there, but is needed, and the parentheses
02820                          * are listed there but doesn't appear to need it.
02821                          */
02822                         $envcmd .= "set $k=" . preg_replace( '/([&|()<>^"])/', '^\\1', $v ) . '&& ';
02823                 } else {
02824                         /* Assume this is a POSIX shell, thus required to accept variable assignments before the command
02825                          * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_09_01
02826                          */
02827                         $envcmd .= "$k=" . escapeshellarg( $v ) . ' ';
02828                 }
02829         }
02830         $cmd = $envcmd . $cmd;
02831 
02832         if ( wfIsWindows() ) {
02833                 if ( version_compare( PHP_VERSION, '5.3.0', '<' ) && /* Fixed in 5.3.0 :) */
02834                         ( version_compare( PHP_VERSION, '5.2.1', '>=' ) || php_uname( 's' ) == 'Windows NT' ) )
02835                 {
02836                         # Hack to work around PHP's flawed invocation of cmd.exe
02837                         # http://news.php.net/php.internals/21796
02838                         # Windows 9x doesn't accept any kind of quotes
02839                         $cmd = '"' . $cmd . '"';
02840                 }
02841         } elseif ( php_uname( 's' ) == 'Linux' ) {
02842                 $time = intval( $wgMaxShellTime );
02843                 $mem = intval( $wgMaxShellMemory );
02844                 $filesize = intval( $wgMaxShellFileSize );
02845 
02846                 if ( $time > 0 && $mem > 0 ) {
02847                         $script = "$IP/bin/ulimit4.sh";
02848                         if ( is_executable( $script ) ) {
02849                                 $cmd = '/bin/bash ' . escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
02850                         }
02851                 }
02852         }
02853         wfDebug( "wfShellExec: $cmd\n" );
02854 
02855         $retval = 1; // error by default?
02856         ob_start();
02857         passthru( $cmd, $retval );
02858         $output = ob_get_contents();
02859         ob_end_clean();
02860 
02861         if ( $retval == 127 ) {
02862                 wfDebugLog( 'exec', "Possibly missing executable file: $cmd\n" );
02863         }
02864         return $output;
02865 }
02866 
02871 function wfInitShellLocale() {
02872         static $done = false;
02873         if ( $done ) {
02874                 return;
02875         }
02876         $done = true;
02877         global $wgShellLocale;
02878         if ( !wfIniGetBool( 'safe_mode' ) ) {
02879                 putenv( "LC_CTYPE=$wgShellLocale" );
02880                 setlocale( LC_CTYPE, $wgShellLocale );
02881         }
02882 }
02883 
02895 function wfShellMaintenanceCmd( $script, array $parameters = array(), array $options = array() ) {
02896         global $wgPhpCli;
02897         // Give site config file a chance to run the script in a wrapper.
02898         // The caller may likely want to call wfBasename() on $script.
02899         wfRunHooks( 'wfShellMaintenanceCmd', array( &$script, &$parameters, &$options ) );
02900         $cmd = isset( $options['php'] ) ? array( $options['php'] ) : array( $wgPhpCli );
02901         if ( isset( $options['wrapper'] ) ) {
02902                 $cmd[] = $options['wrapper'];
02903         }
02904         $cmd[] = $script;
02905         // Escape each parameter for shell
02906         return implode( " ", array_map( 'wfEscapeShellArg', array_merge( $cmd, $parameters ) ) );
02907 }
02908 
02919 function wfMerge( $old, $mine, $yours, &$result ) {
02920         global $wgDiff3;
02921 
02922         # This check may also protect against code injection in
02923         # case of broken installations.
02924         wfSuppressWarnings();
02925         $haveDiff3 = $wgDiff3 && file_exists( $wgDiff3 );
02926         wfRestoreWarnings();
02927 
02928         if( !$haveDiff3 ) {
02929                 wfDebug( "diff3 not found\n" );
02930                 return false;
02931         }
02932 
02933         # Make temporary files
02934         $td = wfTempDir();
02935         $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
02936         $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
02937         $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
02938 
02939         fwrite( $oldtextFile, $old );
02940         fclose( $oldtextFile );
02941         fwrite( $mytextFile, $mine );
02942         fclose( $mytextFile );
02943         fwrite( $yourtextFile, $yours );
02944         fclose( $yourtextFile );
02945 
02946         # Check for a conflict
02947         $cmd = $wgDiff3 . ' -a --overlap-only ' .
02948                 wfEscapeShellArg( $mytextName ) . ' ' .
02949                 wfEscapeShellArg( $oldtextName ) . ' ' .
02950                 wfEscapeShellArg( $yourtextName );
02951         $handle = popen( $cmd, 'r' );
02952 
02953         if( fgets( $handle, 1024 ) ) {
02954                 $conflict = true;
02955         } else {
02956                 $conflict = false;
02957         }
02958         pclose( $handle );
02959 
02960         # Merge differences
02961         $cmd = $wgDiff3 . ' -a -e --merge ' .
02962                 wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
02963         $handle = popen( $cmd, 'r' );
02964         $result = '';
02965         do {
02966                 $data = fread( $handle, 8192 );
02967                 if ( strlen( $data ) == 0 ) {
02968                         break;
02969                 }
02970                 $result .= $data;
02971         } while ( true );
02972         pclose( $handle );
02973         unlink( $mytextName );
02974         unlink( $oldtextName );
02975         unlink( $yourtextName );
02976 
02977         if ( $result === '' && $old !== '' && !$conflict ) {
02978                 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
02979                 $conflict = true;
02980         }
02981         return !$conflict;
02982 }
02983 
02993 function wfDiff( $before, $after, $params = '-u' ) {
02994         if ( $before == $after ) {
02995                 return '';
02996         }
02997 
02998         global $wgDiff;
02999         wfSuppressWarnings();
03000         $haveDiff = $wgDiff && file_exists( $wgDiff );
03001         wfRestoreWarnings();
03002 
03003         # This check may also protect against code injection in
03004         # case of broken installations.
03005         if( !$haveDiff ) {
03006                 wfDebug( "diff executable not found\n" );
03007                 $diffs = new Diff( explode( "\n", $before ), explode( "\n", $after ) );
03008                 $format = new UnifiedDiffFormatter();
03009                 return $format->format( $diffs );
03010         }
03011 
03012         # Make temporary files
03013         $td = wfTempDir();
03014         $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
03015         $newtextFile = fopen( $newtextName = tempnam( $td, 'merge-your-' ), 'w' );
03016 
03017         fwrite( $oldtextFile, $before );
03018         fclose( $oldtextFile );
03019         fwrite( $newtextFile, $after );
03020         fclose( $newtextFile );
03021 
03022         // Get the diff of the two files
03023         $cmd = "$wgDiff " . $params . ' ' . wfEscapeShellArg( $oldtextName, $newtextName );
03024 
03025         $h = popen( $cmd, 'r' );
03026 
03027         $diff = '';
03028 
03029         do {
03030                 $data = fread( $h, 8192 );
03031                 if ( strlen( $data ) == 0 ) {
03032                         break;
03033                 }
03034                 $diff .= $data;
03035         } while ( true );
03036 
03037         // Clean up
03038         pclose( $h );
03039         unlink( $oldtextName );
03040         unlink( $newtextName );
03041 
03042         // Kill the --- and +++ lines. They're not useful.
03043         $diff_lines = explode( "\n", $diff );
03044         if ( strpos( $diff_lines[0], '---' ) === 0 ) {
03045                 unset( $diff_lines[0] );
03046         }
03047         if ( strpos( $diff_lines[1], '+++' ) === 0 ) {
03048                 unset( $diff_lines[1] );
03049         }
03050 
03051         $diff = implode( "\n", $diff_lines );
03052 
03053         return $diff;
03054 }
03055 
03071 function wfUsePHP( $req_ver ) {
03072         $php_ver = PHP_VERSION;
03073 
03074         if ( version_compare( $php_ver, (string)$req_ver, '<' ) ) {
03075                 throw new MWException( "PHP $req_ver required--this is only $php_ver" );
03076         }
03077 }
03078 
03092 function wfUseMW( $req_ver ) {
03093         global $wgVersion;
03094 
03095         if ( version_compare( $wgVersion, (string)$req_ver, '<' ) ) {
03096                 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
03097         }
03098 }
03099 
03112 function wfBaseName( $path, $suffix = '' ) {
03113         $encSuffix = ( $suffix == '' )
03114                 ? ''
03115                 : ( '(?:' . preg_quote( $suffix, '#' ) . ')?' );
03116         $matches = array();
03117         if( preg_match( "#([^/\\\\]*?){$encSuffix}[/\\\\]*$#", $path, $matches ) ) {
03118                 return $matches[1];
03119         } else {
03120                 return '';
03121         }
03122 }
03123 
03133 function wfRelativePath( $path, $from ) {
03134         // Normalize mixed input on Windows...
03135         $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
03136         $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
03137 
03138         // Trim trailing slashes -- fix for drive root
03139         $path = rtrim( $path, DIRECTORY_SEPARATOR );
03140         $from = rtrim( $from, DIRECTORY_SEPARATOR );
03141 
03142         $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
03143         $against = explode( DIRECTORY_SEPARATOR, $from );
03144 
03145         if( $pieces[0] !== $against[0] ) {
03146                 // Non-matching Windows drive letters?
03147                 // Return a full path.
03148                 return $path;
03149         }
03150 
03151         // Trim off common prefix
03152         while( count( $pieces ) && count( $against )
03153                 && $pieces[0] == $against[0] ) {
03154                 array_shift( $pieces );
03155                 array_shift( $against );
03156         }
03157 
03158         // relative dots to bump us to the parent
03159         while( count( $against ) ) {
03160                 array_unshift( $pieces, '..' );
03161                 array_shift( $against );
03162         }
03163 
03164         array_push( $pieces, wfBaseName( $path ) );
03165 
03166         return implode( DIRECTORY_SEPARATOR, $pieces );
03167 }
03168 
03176 function wfDoUpdates( $commit = '' ) {
03177         wfDeprecated( __METHOD__, '1.19' );
03178         DeferredUpdates::doUpdates( $commit );
03179 }
03180 
03195 function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1, $lowercase = true ) {
03196         $input = strval( $input );
03197         if( $sourceBase < 2 ||
03198                 $sourceBase > 36 ||
03199                 $destBase < 2 ||
03200                 $destBase > 36 ||
03201                 $pad < 1 ||
03202                 $sourceBase != intval( $sourceBase ) ||
03203                 $destBase != intval( $destBase ) ||
03204                 $pad != intval( $pad ) ||
03205                 !is_string( $input ) ||
03206                 $input == '' ) {
03207                 return false;
03208         }
03209         $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
03210         $inDigits = array();
03211         $outChars = '';
03212 
03213         // Decode and validate input string
03214         $input = strtolower( $input );
03215         for( $i = 0; $i < strlen( $input ); $i++ ) {
03216                 $n = strpos( $digitChars, $input[$i] );
03217                 if( $n === false || $n > $sourceBase ) {
03218                         return false;
03219                 }
03220                 $inDigits[] = $n;
03221         }
03222 
03223         // Iterate over the input, modulo-ing out an output digit
03224         // at a time until input is gone.
03225         while( count( $inDigits ) ) {
03226                 $work = 0;
03227                 $workDigits = array();
03228 
03229                 // Long division...
03230                 foreach( $inDigits as $digit ) {
03231                         $work *= $sourceBase;
03232                         $work += $digit;
03233 
03234                         if( $work < $destBase ) {
03235                                 // Gonna need to pull another digit.
03236                                 if( count( $workDigits ) ) {
03237                                         // Avoid zero-padding; this lets us find
03238                                         // the end of the input very easily when
03239                                         // length drops to zero.
03240                                         $workDigits[] = 0;
03241                                 }
03242                         } else {
03243                                 // Finally! Actual division!
03244                                 $workDigits[] = intval( $work / $destBase );
03245 
03246                                 // Isn't it annoying that most programming languages
03247                                 // don't have a single divide-and-remainder operator,
03248                                 // even though the CPU implements it that way?
03249                                 $work = $work % $destBase;
03250                         }
03251                 }
03252 
03253                 // All that division leaves us with a remainder,
03254                 // which is conveniently our next output digit.
03255                 $outChars .= $digitChars[$work];
03256 
03257                 // And we continue!
03258                 $inDigits = $workDigits;
03259         }
03260 
03261         while( strlen( $outChars ) < $pad ) {
03262                 $outChars .= '0';
03263         }
03264 
03265         return strrev( $outChars );
03266 }
03267 
03276 function wfCreateObject( $name, $p ) {
03277         wfDeprecated( __FUNCTION__, '1.18' );
03278         return MWFunction::newObj( $name, $p );
03279 }
03280 
03284 function wfHttpOnlySafe() {
03285         global $wgHttpOnlyBlacklist;
03286 
03287         if( isset( $_SERVER['HTTP_USER_AGENT'] ) ) {
03288                 foreach( $wgHttpOnlyBlacklist as $regex ) {
03289                         if( preg_match( $regex, $_SERVER['HTTP_USER_AGENT'] ) ) {
03290                                 return false;
03291                         }
03292                 }
03293         }
03294 
03295         return true;
03296 }
03297 
03307 function wfCheckEntropy() {
03308         return (
03309                         ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
03310                         || ini_get( 'session.entropy_file' )
03311                 )
03312                 && intval( ini_get( 'session.entropy_length' ) ) >= 32;
03313 }
03314 
03319 function wfFixSessionID() {
03320         // If the cookie or session id is already set we already have a session and should abort
03321         if ( isset( $_COOKIE[ session_name() ] ) || session_id() ) {
03322                 return;
03323         }
03324 
03325         $entropyEnabled = wfCheckEntropy();
03326 
03327         // If built-in entropy is not enabled or not sufficient override php's built in session id generation code
03328         if ( !$entropyEnabled ) {
03329                 wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, overriding session id generation using our cryptrand source.\n" );
03330                 session_id( MWCryptRand::generateHex( 32 ) );
03331         }
03332 }
03333 
03339 function wfSetupSession( $sessionId = false ) {
03340         global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain,
03341                         $wgCookieSecure, $wgCookieHttpOnly, $wgSessionHandler;
03342         if( $wgSessionsInMemcached ) {
03343                 if ( !defined( 'MW_COMPILED' ) ) {
03344                         global $IP;
03345                         require_once( "$IP/includes/cache/MemcachedSessions.php" );
03346                 }
03347                 session_set_save_handler( 'memsess_open', 'memsess_close', 'memsess_read',
03348                         'memsess_write', 'memsess_destroy', 'memsess_gc' );
03349 
03350                 // It's necessary to register a shutdown function to call session_write_close(),
03351                 // because by the time the request shutdown function for the session module is
03352                 // called, $wgMemc has already been destroyed. Shutdown functions registered
03353                 // this way are called before object destruction.
03354                 register_shutdown_function( 'memsess_write_close' );
03355         } elseif( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
03356                 # Only set this if $wgSessionHandler isn't null and session.save_handler
03357                 # hasn't already been set to the desired value (that causes errors)
03358                 ini_set( 'session.save_handler', $wgSessionHandler );
03359         }
03360         $httpOnlySafe = wfHttpOnlySafe() && $wgCookieHttpOnly;
03361         wfDebugLog( 'cookie',
03362                 'session_set_cookie_params: "' . implode( '", "',
03363                         array(
03364                                 0,
03365                                 $wgCookiePath,
03366                                 $wgCookieDomain,
03367                                 $wgCookieSecure,
03368                                 $httpOnlySafe ) ) . '"' );
03369         session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $httpOnlySafe );
03370         session_cache_limiter( 'private, must-revalidate' );
03371         if ( $sessionId ) {
03372                 session_id( $sessionId );
03373         } else {
03374                 wfFixSessionID();
03375         }
03376         wfSuppressWarnings();
03377         session_start();
03378         wfRestoreWarnings();
03379 }
03380 
03387 function wfGetPrecompiledData( $name ) {
03388         global $IP;
03389 
03390         $file = "$IP/serialized/$name";
03391         if ( file_exists( $file ) ) {
03392                 $blob = file_get_contents( $file );
03393                 if ( $blob ) {
03394                         return unserialize( $blob );
03395                 }
03396         }
03397         return false;
03398 }
03399 
03406 function wfMemcKey( /*... */ ) {
03407         global $wgCachePrefix;
03408         $prefix = $wgCachePrefix === false ? wfWikiID() : $wgCachePrefix;
03409         $args = func_get_args();
03410         $key = $prefix . ':' . implode( ':', $args );
03411         $key = str_replace( ' ', '_', $key );
03412         return $key;
03413 }
03414 
03423 function wfForeignMemcKey( $db, $prefix /*, ... */ ) {
03424         $args = array_slice( func_get_args(), 2 );
03425         if ( $prefix ) {
03426                 $key = "$db-$prefix:" . implode( ':', $args );
03427         } else {
03428                 $key = $db . ':' . implode( ':', $args );
03429         }
03430         return $key;
03431 }
03432 
03439 function wfWikiID() {
03440         global $wgDBprefix, $wgDBname;
03441         if ( $wgDBprefix ) {
03442                 return "$wgDBname-$wgDBprefix";
03443         } else {
03444                 return $wgDBname;
03445         }
03446 }
03447 
03455 function wfSplitWikiID( $wiki ) {
03456         $bits = explode( '-', $wiki, 2 );
03457         if ( count( $bits ) < 2 ) {
03458                 $bits[] = '';
03459         }
03460         return $bits;
03461 }
03462 
03485 function &wfGetDB( $db, $groups = array(), $wiki = false ) {
03486         return wfGetLB( $wiki )->getConnection( $db, $groups, $wiki );
03487 }
03488 
03495 function wfGetLB( $wiki = false ) {
03496         return wfGetLBFactory()->getMainLB( $wiki );
03497 }
03498 
03504 function &wfGetLBFactory() {
03505         return LBFactory::singleton();
03506 }
03507 
03528 function wfFindFile( $title, $options = array() ) {
03529         return RepoGroup::singleton()->findFile( $title, $options );
03530 }
03531 
03539 function wfLocalFile( $title ) {
03540         return RepoGroup::singleton()->getLocalRepo()->newFile( $title );
03541 }
03542 
03547 function wfStreamFile( $fname, $headers = array() ) {
03548         wfDeprecated( __FUNCTION__, '1.19' );
03549         StreamFile::stream( $fname, $headers );
03550 }
03551 
03558 function wfQueriesMustScale() {
03559         global $wgMiserMode;
03560         return $wgMiserMode
03561                 || ( SiteStats::pages() > 100000
03562                 && SiteStats::edits() > 1000000
03563                 && SiteStats::users() > 10000 );
03564 }
03565 
03573 function wfScript( $script = 'index' ) {
03574         global $wgScriptPath, $wgScriptExtension;
03575         return "{$wgScriptPath}/{$script}{$wgScriptExtension}";
03576 }
03577 
03583 function wfGetScriptUrl() {
03584         if( isset( $_SERVER['SCRIPT_NAME'] ) ) {
03585                 #
03586                 # as it was called, minus the query string.
03587                 #
03588                 # Some sites use Apache rewrite rules to handle subdomains,
03589                 # and have PHP set up in a weird way that causes PHP_SELF
03590                 # to contain the rewritten URL instead of the one that the
03591                 # outside world sees.
03592                 #
03593                 # If in this mode, use SCRIPT_URL instead, which mod_rewrite
03594                 # provides containing the "before" URL.
03595                 return $_SERVER['SCRIPT_NAME'];
03596         } else {
03597                 return $_SERVER['URL'];
03598         }
03599 }
03600 
03608 function wfBoolToStr( $value ) {
03609         return $value ? 'true' : 'false';
03610 }
03611 
03618 function wfLoadExtensionMessages() {
03619         wfDeprecated( __FUNCTION__, '1.16' );
03620 }
03621 
03627 function wfGetNull() {
03628         return wfIsWindows()
03629                 ? 'NUL'
03630                 : '/dev/null';
03631 }
03632 
03643 function wfWaitForSlaves( $maxLag = false, $wiki = false ) {
03644         $lb = wfGetLB( $wiki );
03645         // bug 27975 - Don't try to wait for slaves if there are none
03646         // Prevents permission error when getting master position
03647         if ( $lb->getServerCount() > 1 ) {
03648                 $dbw = $lb->getConnection( DB_MASTER );
03649                 $pos = $dbw->getMasterPos();
03650                 $lb->waitForAll( $pos );
03651         }
03652 }
03653 
03658 function wfOut( $s ) {
03659         wfDeprecated( __FUNCTION__, '1.18' );
03660         global $wgCommandLineMode;
03661         if ( $wgCommandLineMode ) {
03662                 echo $s;
03663         } else {
03664                 echo htmlspecialchars( $s );
03665         }
03666         flush();
03667 }
03668 
03675 function wfCountDown( $n ) {
03676         for ( $i = $n; $i >= 0; $i-- ) {
03677                 if ( $i != $n ) {
03678                         echo str_repeat( "\x08", strlen( $i + 1 ) );
03679                 }
03680                 echo $i;
03681                 flush();
03682                 if ( $i ) {
03683                         sleep( 1 );
03684                 }
03685         }
03686         echo "\n";
03687 }
03688 
03696 function wfGenerateToken( $salt = '' ) {
03697         $salt = serialize( $salt );
03698         return md5( mt_rand( 0, 0x7fffffff ) . $salt );
03699 }
03700 
03707 function wfStripIllegalFilenameChars( $name ) {
03708         global $wgIllegalFileChars;
03709         $name = wfBaseName( $name );
03710         $name = preg_replace(
03711                 "/[^" . Title::legalChars() . "]" .
03712                         ( $wgIllegalFileChars ? "|[" . $wgIllegalFileChars . "]" : '' ) .
03713                         "/",
03714                 '-',
03715                 $name
03716         );
03717         return $name;
03718 }
03719 
03725 function wfMemoryLimit() {
03726         global $wgMemoryLimit;
03727         $memlimit = wfShorthandToInteger( ini_get( 'memory_limit' ) );
03728         if( $memlimit != -1 ) {
03729                 $conflimit = wfShorthandToInteger( $wgMemoryLimit );
03730                 if( $conflimit == -1 ) {
03731                         wfDebug( "Removing PHP's memory limit\n" );
03732                         wfSuppressWarnings();
03733                         ini_set( 'memory_limit', $conflimit );
03734                         wfRestoreWarnings();
03735                         return $conflimit;
03736                 } elseif ( $conflimit > $memlimit ) {
03737                         wfDebug( "Raising PHP's memory limit to $conflimit bytes\n" );
03738                         wfSuppressWarnings();
03739                         ini_set( 'memory_limit', $conflimit );
03740                         wfRestoreWarnings();
03741                         return $conflimit;
03742                 }
03743         }
03744         return $memlimit;
03745 }
03746 
03753 function wfShorthandToInteger( $string = '' ) {
03754         $string = trim( $string );
03755         if( $string === '' ) {
03756                 return -1;
03757         }
03758         $last = $string[strlen( $string ) - 1];
03759         $val = intval( $string );
03760         switch( $last ) {
03761                 case 'g':
03762                 case 'G':
03763                         $val *= 1024;
03764                         // break intentionally missing
03765                 case 'm':
03766                 case 'M':
03767                         $val *= 1024;
03768                         // break intentionally missing
03769                 case 'k':
03770                 case 'K':
03771                         $val *= 1024;
03772         }
03773 
03774         return $val;
03775 }
03776 
03784 function wfBCP47( $code ) {
03785         $codeSegment = explode( '-', $code );
03786         $codeBCP = array();
03787         foreach ( $codeSegment as $segNo => $seg ) {
03788                 if ( count( $codeSegment ) > 0 ) {
03789                         // when previous segment is x, it is a private segment and should be lc
03790                         if( $segNo > 0 && strtolower( $codeSegment[( $segNo - 1 )] ) == 'x' ) {
03791                                 $codeBCP[$segNo] = strtolower( $seg );
03792                         // ISO 3166 country code
03793                         } elseif ( ( strlen( $seg ) == 2 ) && ( $segNo > 0 ) ) {
03794                                 $codeBCP[$segNo] = strtoupper( $seg );
03795                         // ISO 15924 script code
03796                         } elseif ( ( strlen( $seg ) == 4 ) && ( $segNo > 0 ) ) {
03797                                 $codeBCP[$segNo] = ucfirst( strtolower( $seg ) );
03798                         // Use lowercase for other cases
03799                         } else {
03800                                 $codeBCP[$segNo] = strtolower( $seg );
03801                         }
03802                 } else {
03803                 // Use lowercase for single segment
03804                         $codeBCP[$segNo] = strtolower( $seg );
03805                 }
03806         }
03807         $langCode = implode( '-', $codeBCP );
03808         return $langCode;
03809 }
03810 
03817 function wfGetCache( $inputType ) {
03818         return ObjectCache::getInstance( $inputType );
03819 }
03820 
03826 function wfGetMainCache() {
03827         global $wgMainCacheType;
03828         return ObjectCache::getInstance( $wgMainCacheType );
03829 }
03830 
03836 function wfGetMessageCacheStorage() {
03837         global $wgMessageCacheType;
03838         return ObjectCache::getInstance( $wgMessageCacheType );
03839 }
03840 
03846 function wfGetParserCacheStorage() {
03847         global $wgParserCacheType;
03848         return ObjectCache::getInstance( $wgParserCacheType );
03849 }
03850 
03858 function wfRunHooks( $event, $args = array() ) {
03859         return Hooks::run( $event, $args );
03860 }
03861 
03876 function wfUnpack( $format, $data, $length=false ) {
03877         if ( $length !== false ) {
03878                 $realLen = strlen( $data );
03879                 if ( $realLen < $length ) {
03880                         throw new MWException( "Tried to use wfUnpack on a "
03881                                 . "string of length $realLen, but needed one "
03882                                 . "of at least length $length."
03883                         );
03884                 }
03885         }
03886 
03887         wfSuppressWarnings();
03888         $result = unpack( $format, $data );
03889         wfRestoreWarnings();
03890 
03891         if ( $result === false ) {
03892                 // If it cannot extract the packed data.
03893                 throw new MWException( "unpack could not unpack binary data" );
03894         }
03895         return $result;
03896 }