<?php
+if ( !defined( 'MEDIAWIKI' ) ) {
+ die( "This file is part of MediaWiki, it is not a valid entry point" );
+}
+
/**
* Global functions used everywhere
- * @package MediaWiki
*/
/**
* Some globals and requires needed
*/
-/**
- * Total number of articles
- * @global integer $wgNumberOfArticles
- */
+/** Total number of articles */
$wgNumberOfArticles = -1; # Unset
-/**
- * Total number of views
- * @global integer $wgTotalViews
- */
+
+/** Total number of views */
$wgTotalViews = -1;
-/**
- * Total number of edits
- * @global integer $wgTotalEdits
- */
+
+/** Total number of edits */
$wgTotalEdits = -1;
-require_once( 'LogPage.php' );
-require_once( 'normal/UtfNormalUtil.php' );
-require_once( 'XmlFunctions.php' );
+require_once dirname(__FILE__) . '/LogPage.php';
+require_once dirname(__FILE__) . '/normal/UtfNormalUtil.php';
+require_once dirname(__FILE__) . '/XmlFunctions.php';
/**
* Compatibility functions
}
}
+if ( !function_exists( 'mb_strlen' ) ) {
+ /**
+ * Fallback implementation of mb_strlen, hardcoded to UTF-8.
+ * @param string $str
+ * @param string $enc optional encoding; ignored
+ * @return int
+ */
+ function mb_strlen( $str, $enc="" ) {
+ $counts = count_chars( $str );
+ $total = 0;
+
+ // Count ASCII bytes
+ for( $i = 0; $i < 0x80; $i++ ) {
+ $total += $counts[$i];
+ }
+
+ // Count multibyte sequence heads
+ for( $i = 0xc0; $i < 0xff; $i++ ) {
+ $total += $counts[$i];
+ }
+ return $total;
+ }
+}
+
if ( !function_exists( 'array_diff_key' ) ) {
/**
* Exists in PHP 5.1.0+
function wfRandom() {
# The maximum random value is "only" 2^31-1, so get two random
# values to reduce the chance of dupes
- $max = mt_getrandmax();
+ $max = mt_getrandmax() + 1;
$rand = number_format( (mt_rand() * $max + mt_rand())
/ $max / $max, 12, '.', '' );
return $rand;
# Strip unprintables; they can switch terminal modes when binary data
# gets dumped, which is pretty annoying.
$text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
- @error_log( $text, 3, $wgDebugLogFile );
+ wfErrorLog( $text, $wgDebugLogFile );
}
}
if( isset( $wgDebugLogGroups[$logGroup] ) ) {
$time = wfTimestamp( TS_DB );
$wiki = wfWikiID();
- @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
+ wfErrorLog( "$time $wiki: $text", $wgDebugLogGroups[$logGroup] );
} else if ( $public === true ) {
wfDebug( $text, true );
}
* @param $text String: database error message.
*/
function wfLogDBError( $text ) {
- global $wgDBerrorLog;
+ global $wgDBerrorLog, $wgDBname;
if ( $wgDBerrorLog ) {
$host = trim(`hostname`);
- $text = date('D M j G:i:s T Y') . "\t$host\t".$text;
- error_log( $text, 3, $wgDBerrorLog );
+ $text = date('D M j G:i:s T Y') . "\t$host\t$wgDBname\t$text";
+ wfErrorLog( $text, $wgDBerrorLog );
}
}
+/**
+ * Log to a file without getting "file size exceeded" signals
+ */
+function wfErrorLog( $text, $file ) {
+ wfSuppressWarnings();
+ $exists = file_exists( $file );
+ $size = $exists ? filesize( $file ) : false;
+ if ( !$exists || ( $size !== false && $size + strlen( $text ) < 0x7fffffff ) ) {
+ error_log( $text, 3, $file );
+ }
+ wfRestoreWarnings();
+}
+
/**
* @todo document
*/
$forward .= ' anon';
$log = sprintf( "%s\t%04.3f\t%s\n",
gmdate( 'YmdHis' ), $elapsed,
- urldecode( $_SERVER['REQUEST_URI'] . $forward ) );
+ urldecode( $wgRequest->getRequestURL() . $forward ) );
if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
- error_log( $log . $prof, 3, $wgDebugLogFile );
+ wfErrorLog( $log . $prof, $wgDebugLogFile );
}
}
}
function wfMsgNoTrans( $key ) {
$args = func_get_args();
array_shift( $args );
- return wfMsgReal( $key, $args, true, false );
+ return wfMsgReal( $key, $args, true, false, false );
}
/**
* @return String: the requested message.
*/
function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) {
+ $fname = 'wfMsgReal';
+ wfProfileIn( $fname );
$message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
$message = wfMsgReplaceArgs( $message, $args );
+ wfProfileOut( $fname );
return $message;
}
* Returns message in the requested format
* @param string $key Key of the message
* @param array $options Processing rules:
- * <i>parse<i>: parses wikitext to html
- * <i>parseinline<i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
- * <i>escape<i>: filters message trough htmlspecialchars
- * <i>replaceafter<i>: parameters are substituted after parsing or escaping
- * <i>parsemag<i>: ??
+ * <i>parse</i>: parses wikitext to html
+ * <i>parseinline</i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
+ * <i>escape</i>: filters message trough htmlspecialchars
+ * <i>replaceafter</i>: parameters are substituted after parsing or escaping
+ * <i>parsemag</i>: transform the message using magic phrases
*/
function wfMsgExt( $key, $options ) {
global $wgOut, $wgParser;
$string = $m[1];
}
} elseif ( in_array('parsemag', $options) ) {
- global $wgTitle;
- $parser = new Parser();
- $parserOptions = new ParserOptions();
- $parserOptions->setInterfaceMessage( true );
- $parser->startExternalParse( $wgTitle, $parserOptions, OT_MSG );
- $string = $parser->transformMsg( $string, $parserOptions );
+ global $wgMessageCache;
+ if ( isset( $wgMessageCache ) ) {
+ $string = $wgMessageCache->transform( $string );
+ }
}
if ( in_array('escape', $options) ) {
* Just like exit() but makes a note of it.
* Commits open transactions except if the error parameter is set
*
- * @obsolete Please return control to the caller or throw an exception
+ * @deprecated Please return control to the caller or throw an exception
*/
function wfAbruptExit( $error = false ){
global $wgLoadBalancer;
}
$called = true;
- if( function_exists( 'debug_backtrace' ) ){ // PHP >= 4.3
- $bt = debug_backtrace();
+ $bt = wfDebugBacktrace();
+ if( $bt ) {
for($i = 0; $i < count($bt) ; $i++){
$file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown";
$line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown";
}
/**
- * @obsolete Please return control the caller or throw an exception
+ * @deprecated Please return control the caller or throw an exception
*/
function wfErrorExit() {
wfAbruptExit( true );
return $com;
}
+/**
+ * Safety wrapper for debug_backtrace().
+ *
+ * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat
+ * murky circumstances, which may be triggered in part by stub objects
+ * or other fancy talkin'.
+ *
+ * Will return an empty array if Zend Optimizer is detected, otherwise
+ * the output from debug_backtrace() (trimmed).
+ *
+ * @return array of backtrace information
+ */
+function wfDebugBacktrace() {
+ if( extension_loaded( 'Zend Optimizer' ) ) {
+ wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
+ return array();
+ } else {
+ return array_slice( debug_backtrace(), 1 );
+ }
+}
+
function wfBacktrace() {
global $wgCommandLineMode;
- if ( !function_exists( 'debug_backtrace' ) ) {
- return false;
- }
if ( $wgCommandLineMode ) {
$msg = '';
} else {
$msg = "<ul>\n";
}
- $backtrace = debug_backtrace();
+ $backtrace = wfDebugBacktrace();
foreach( $backtrace as $call ) {
if( isset( $call['file'] ) ) {
$f = explode( DIRECTORY_SEPARATOR, $call['file'] );
*/
function wfShowingResults( $offset, $limit ) {
global $wgLang;
- return wfMsg( 'showingresults', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
+ return wfMsgExt( 'showingresults', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
}
/**
*/
function wfShowingResultsNum( $offset, $limit, $num ) {
global $wgLang;
- return wfMsg( 'showingresultsnum', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
+ return wfMsgExt( 'showingresultsnum', array( 'parseinline' ), $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
}
/**
return $cgi;
}
+/**
+ * Append a query string to an existing URL, which may or may not already
+ * have query string parameters already. If so, they will be combined.
+ *
+ * @param string $url
+ * @param string $query
+ * @return string
+ */
+function wfAppendQuery( $url, $query ) {
+ if( $query != '' ) {
+ if( false === strpos( $url, '?' ) ) {
+ $url .= '?';
+ } else {
+ $url .= '&';
+ }
+ $url .= $query;
+ }
+ return $url;
+}
+
/**
* This is obsolete, use SquidUpdate::purge()
* @deprecated
header( "Status: $code $label" );
$wgOut->sendCacheControl();
- header( 'Content-type: text/html' );
- print "<html><head><title>" .
+ header( 'Content-type: text/html; charset=utf-8' );
+ print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">".
+ "<html><head><title>" .
htmlspecialchars( $label ) .
"</title></head><body><h1>" .
htmlspecialchars( $label ) .
"</h1><p>" .
- htmlspecialchars( $desc ) .
+ nl2br( htmlspecialchars( $desc ) ) .
"</p></body></html>\n";
}
+/**
+ * Clear away any user-level output buffers, discarding contents.
+ *
+ * Suitable for 'starting afresh', for instance when streaming
+ * relatively large amounts of data without buffering, or wanting to
+ * output image files without ob_gzhandler's compression.
+ *
+ * The optional $resetGzipEncoding parameter controls suppression of
+ * the Content-Encoding header sent by ob_gzhandler; by default it
+ * is left. See comments for wfClearOutputBuffers() for why it would
+ * be used.
+ *
+ * Note that some PHP configuration options may add output buffer
+ * layers which cannot be removed; these are left in place.
+ *
+ * @param bool $resetGzipEncoding
+ */
+function wfResetOutputBuffers( $resetGzipEncoding=true ) {
+ if( $resetGzipEncoding ) {
+ // Suppress Content-Encoding and Content-Length
+ // headers from 1.10+s wfOutputHandler
+ global $wgDisableOutputCompression;
+ $wgDisableOutputCompression = true;
+ }
+ while( $status = ob_get_status() ) {
+ if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
+ // Probably from zlib.output_compression or other
+ // PHP-internal setting which can't be removed.
+ //
+ // Give up, and hope the result doesn't break
+ // output behavior.
+ break;
+ }
+ if( !ob_end_clean() ) {
+ // Could not remove output buffer handler; abort now
+ // to avoid getting in some kind of infinite loop.
+ break;
+ }
+ if( $resetGzipEncoding ) {
+ if( $status['name'] == 'ob_gzhandler' ) {
+ // Reset the 'Content-Encoding' field set by this handler
+ // so we can start fresh.
+ header( 'Content-Encoding:' );
+ }
+ }
+ }
+}
+
+/**
+ * More legible than passing a 'false' parameter to wfResetOutputBuffers():
+ *
+ * Clear away output buffers, but keep the Content-Encoding header
+ * produced by ob_gzhandler, if any.
+ *
+ * This should be used for HTTP 304 responses, where you need to
+ * preserve the Content-Encoding header of the real result, but
+ * also need to suppress the output of ob_gzhandler to keep to spec
+ * and avoid breaking Firefox in rare cases where the headers and
+ * body are broken over two packets.
+ */
+function wfClearOutputBuffers() {
+ wfResetOutputBuffers( false );
+}
+
/**
* Converts an Accept-* header into an array mapping string values to quality
* factors
/**
* An Exif timestamp (YYYY:MM:DD HH:MM:SS)
*
- * @url http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
+ * @see http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
* DateTime tag and page 36 for the DateTimeOriginal and
* DateTimeDigitized tags.
*/
wfProfileIn( $fname );
$needParse = false;
- $notice = wfMsgForContent( $name );
- if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
- wfProfileOut( $fname );
- return( false );
+
+ if( $name === 'default' ) {
+ // special case
+ global $wgSiteNotice;
+ $notice = $wgSiteNotice;
+ if( empty( $notice ) ) {
+ wfProfileOut( $fname );
+ return false;
+ }
+ } else {
+ $notice = wfMsgForContentNoTrans( $name );
+ if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
+ wfProfileOut( $fname );
+ return( false );
+ }
}
$cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) {
if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) {
$siteNotice = wfGetCachedNotice( 'sitenotice' );
- $siteNotice = !$siteNotice ? $wgSiteNotice : $siteNotice;
} else {
$anonNotice = wfGetCachedNotice( 'anonnotice' );
if( !$anonNotice ) {
$siteNotice = wfGetCachedNotice( 'sitenotice' );
- $siteNotice = !$siteNotice ? $wgSiteNotice : $siteNotice;
} else {
$siteNotice = $anonNotice;
}
}
+ if( !$siteNotice ) {
+ $siteNotice = wfGetCachedNotice( 'default' );
+ }
}
wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) );
foreach ( $createList as $dir ) {
# use chmod to override the umask, as suggested by the PHP manual
if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
+ wfDebugLog( 'mkdir', "Unable to create directory $dir\n" );
return false;
}
}
}
if ( php_uname( 's' ) == 'Linux' ) {
- $time = ini_get( 'max_execution_time' );
+ $time = intval( ini_get( 'max_execution_time' ) );
$mem = intval( $wgMaxShellMemory );
$filesize = intval( $wgMaxShellFileSize );
if ( $time > 0 && $mem > 0 ) {
- $script = "$IP/bin/ulimit-tvf.sh";
+ $script = "$IP/bin/ulimit4.sh";
if ( is_executable( $script ) ) {
- $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
+ $cmd = escapeshellarg( $script ) . " $time $mem $filesize " . escapeshellarg( $cmd );
}
}
} elseif ( php_uname( 's' ) == 'Windows NT' ) {
}
}
+/**
+ * Generate a relative path name to the given file.
+ * May explode on non-matching case-insensitive paths,
+ * funky symlinks, etc.
+ *
+ * @param string $path Absolute destination path including target filename
+ * @param string $from Absolute source path, directory only
+ * @return string
+ */
+function wfRelativePath( $path, $from ) {
+ // Normalize mixed input on Windows...
+ $path = str_replace( '/', DIRECTORY_SEPARATOR, $path );
+ $from = str_replace( '/', DIRECTORY_SEPARATOR, $from );
+
+ $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
+ $against = explode( DIRECTORY_SEPARATOR, $from );
+
+ // Trim off common prefix
+ while( count( $pieces ) && count( $against )
+ && $pieces[0] == $against[0] ) {
+ array_shift( $pieces );
+ array_shift( $against );
+ }
+
+ // relative dots to bump us to the parent
+ while( count( $against ) ) {
+ array_unshift( $pieces, '..' );
+ array_shift( $against );
+ }
+
+ array_push( $pieces, wfBaseName( $path ) );
+
+ return implode( DIRECTORY_SEPARATOR, $pieces );
+}
+
/**
* Make a URL index, appropriate for the el_index field of externallinks.
*/
function wfMakeUrlIndex( $url ) {
- wfSuppressWarnings();
+ global $wgUrlProtocols; // Allow all protocols defined in DefaultSettings/LocalSettings.php
$bits = parse_url( $url );
+ wfSuppressWarnings();
wfRestoreWarnings();
- if ( !$bits || $bits['scheme'] !== 'http' ) {
+ if ( !$bits ) {
+ return false;
+ }
+ // most of the protocols are followed by ://, but mailto: and sometimes news: not, check for it
+ $delimiter = '';
+ if ( in_array( $bits['scheme'] . '://' , $wgUrlProtocols ) ) {
+ $delimiter = '://';
+ } elseif ( in_array( $bits['scheme'] .':' , $wgUrlProtocols ) ) {
+ $delimiter = ':';
+ // parse_url detects for news: and mailto: the host part of an url as path
+ // We have to correct this wrong detection
+ if ( isset ( $bits['path'] ) ) {
+ $bits['host'] = $bits['path'];
+ $bits['path'] = '';
+ }
+ } else {
return false;
}
+
// Reverse the labels in the hostname, convert to lower case
- $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ // For emails reverse domainpart only
+ if ( $bits['scheme'] == 'mailto' ) {
+ $mailparts = explode( '@', $bits['host'] );
+ $domainpart = strtolower( implode( '.', array_reverse( explode( '.', $mailparts[1] ) ) ) );
+ $reversedHost = $domainpart . '@' . $mailparts[0];
+ } else {
+ $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
+ }
// Add an extra dot to the end
if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
$reversedHost .= '.';
}
// Reconstruct the pseudo-URL
- $index = "http://$reversedHost";
+ $prot = $bits['scheme'];
+ $index = "$prot$delimiter$reversedHost";
// Leave out user and password. Add the port, path, query and fragment
if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port'];
if ( isset( $bits['path'] ) ) {
* @param $sourceBase int 2-36
* @param $destBase int 2-36
* @param $pad int 1 or greater
+ * @param $lowercase bool
* @return string or false on invalid input
*/
-function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
+function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1, $lowercase=true ) {
+ $input = strval( $input );
if( $sourceBase < 2 ||
$sourceBase > 36 ||
$destBase < 2 ||
$input == '' ) {
return false;
}
-
- $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
+ $digitChars = ( $lowercase ) ? '0123456789abcdefghijklmnopqrstuvwxyz' : '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$inDigits = array();
$outChars = '';
* Initialise php session
*/
function wfSetupSession() {
- global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
+ global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain, $wgCookieSecure;
if( $wgSessionsInMemcached ) {
require_once( 'MemcachedSessions.php' );
} elseif( 'files' != ini_get( 'session.save_handler' ) ) {
# application, it will end up failing. Try to recover.
ini_set ( 'session.save_handler', 'files' );
}
- session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
+ session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure);
session_cache_limiter( 'private, must-revalidate' );
@session_start();
}
}
function wfGetCaller( $level = 2 ) {
- $backtrace = debug_backtrace();
+ $backtrace = wfDebugBacktrace();
if ( isset( $backtrace[$level] ) ) {
if ( isset( $backtrace[$level]['class'] ) ) {
$caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
$frame["class"]."::".$frame["function"]:
$frame["function"];
'),
- array_reverse(debug_backtrace())));
+ array_reverse(wfDebugBacktrace())));
}
/**
* master (for write queries), DB_SLAVE for potentially lagged
* read queries, or an integer >= 0 for a particular server.
*
- * @param array $groups Query groups. A list of group names that this query
- * belongs to.
+ * @param mixed $groups Query groups. An array of group names that this query
+ * belongs to. May contain a single string if the query is only
+ * in one group.
*/
function &wfGetDB( $db = DB_LAST, $groups = array() ) {
global $wgLoadBalancer;
- $ret =& $wgLoadBalancer->getConnection( $db, true, $groups );
+ $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
return $ret;
}
?>