* (bug 3000) Fall back to SCRIPT_NAME plus QUERY_STRING when REQUEST_URI is
[lhc/web/wiklou.git] / includes / GlobalFunctions.php
1 <?php
2
3 /**
4 * Global functions used everywhere
5 * @package MediaWiki
6 */
7
8 /**
9 * Some globals and requires needed
10 */
11
12 /** Total number of articles */
13 $wgNumberOfArticles = -1; # Unset
14
15 /** Total number of views */
16 $wgTotalViews = -1;
17
18 /** Total number of edits */
19 $wgTotalEdits = -1;
20
21
22 require_once( 'LogPage.php' );
23 require_once( 'normal/UtfNormalUtil.php' );
24 require_once( 'XmlFunctions.php' );
25
26 /**
27 * Compatibility functions
28 *
29 * We more or less support PHP 5.0.x and up.
30 * Re-implementations of newer functions or functions in non-standard
31 * PHP extensions may be included here.
32 */
33 if( !function_exists('iconv') ) {
34 # iconv support is not in the default configuration and so may not be present.
35 # Assume will only ever use utf-8 and iso-8859-1.
36 # This will *not* work in all circumstances.
37 function iconv( $from, $to, $string ) {
38 if(strcasecmp( $from, $to ) == 0) return $string;
39 if(strcasecmp( $from, 'utf-8' ) == 0) return utf8_decode( $string );
40 if(strcasecmp( $to, 'utf-8' ) == 0) return utf8_encode( $string );
41 return $string;
42 }
43 }
44
45 # UTF-8 substr function based on a PHP manual comment
46 if ( !function_exists( 'mb_substr' ) ) {
47 function mb_substr( $str, $start ) {
48 $ar = array();
49 preg_match_all( '/./us', $str, $ar );
50
51 if( func_num_args() >= 3 ) {
52 $end = func_get_arg( 2 );
53 return join( '', array_slice( $ar[0], $start, $end ) );
54 } else {
55 return join( '', array_slice( $ar[0], $start ) );
56 }
57 }
58 }
59
60 if ( !function_exists( 'array_diff_key' ) ) {
61 /**
62 * Exists in PHP 5.1.0+
63 * Not quite compatible, two-argument version only
64 * Null values will cause problems due to this use of isset()
65 */
66 function array_diff_key( $left, $right ) {
67 $result = $left;
68 foreach ( $left as $key => $unused ) {
69 if ( isset( $right[$key] ) ) {
70 unset( $result[$key] );
71 }
72 }
73 return $result;
74 }
75 }
76
77
78 /**
79 * Wrapper for clone(), for compatibility with PHP4-friendly extensions.
80 * PHP 5 won't let you declare a 'clone' function, even conditionally,
81 * so it has to be a wrapper with a different name.
82 */
83 function wfClone( $object ) {
84 return clone( $object );
85 }
86
87 /**
88 * Where as we got a random seed
89 */
90 $wgRandomSeeded = false;
91
92 /**
93 * Seed Mersenne Twister
94 * No-op for compatibility; only necessary in PHP < 4.2.0
95 */
96 function wfSeedRandom() {
97 /* No-op */
98 }
99
100 /**
101 * Get a random decimal value between 0 and 1, in a way
102 * not likely to give duplicate values for any realistic
103 * number of articles.
104 *
105 * @return string
106 */
107 function wfRandom() {
108 # The maximum random value is "only" 2^31-1, so get two random
109 # values to reduce the chance of dupes
110 $max = mt_getrandmax() + 1;
111 $rand = number_format( (mt_rand() * $max + mt_rand())
112 / $max / $max, 12, '.', '' );
113 return $rand;
114 }
115
116 /**
117 * We want / and : to be included as literal characters in our title URLs.
118 * %2F in the page titles seems to fatally break for some reason.
119 *
120 * @param $s String:
121 * @return string
122 */
123 function wfUrlencode ( $s ) {
124 $s = urlencode( $s );
125 $s = preg_replace( '/%3[Aa]/', ':', $s );
126 $s = preg_replace( '/%2[Ff]/', '/', $s );
127
128 return $s;
129 }
130
131 /**
132 * Sends a line to the debug log if enabled or, optionally, to a comment in output.
133 * In normal operation this is a NOP.
134 *
135 * Controlling globals:
136 * $wgDebugLogFile - points to the log file
137 * $wgProfileOnly - if set, normal debug messages will not be recorded.
138 * $wgDebugRawPage - if false, 'action=raw' hits will not result in debug output.
139 * $wgDebugComments - if on, some debug items may appear in comments in the HTML output.
140 *
141 * @param $text String
142 * @param $logonly Bool: set true to avoid appearing in HTML when $wgDebugComments is set
143 */
144 function wfDebug( $text, $logonly = false ) {
145 global $wgOut, $wgDebugLogFile, $wgDebugComments, $wgProfileOnly, $wgDebugRawPage;
146 static $recursion = 0;
147
148 # Check for raw action using $_GET not $wgRequest, since the latter might not be initialised yet
149 if ( isset( $_GET['action'] ) && $_GET['action'] == 'raw' && !$wgDebugRawPage ) {
150 return;
151 }
152
153 if ( $wgDebugComments && !$logonly ) {
154 if ( !isset( $wgOut ) ) {
155 return;
156 }
157 if ( !StubObject::isRealObject( $wgOut ) ) {
158 if ( $recursion ) {
159 return;
160 }
161 $recursion++;
162 $wgOut->_unstub();
163 $recursion--;
164 }
165 $wgOut->debug( $text );
166 }
167 if ( '' != $wgDebugLogFile && !$wgProfileOnly ) {
168 # Strip unprintables; they can switch terminal modes when binary data
169 # gets dumped, which is pretty annoying.
170 $text = preg_replace( '![\x00-\x08\x0b\x0c\x0e-\x1f]!', ' ', $text );
171 @error_log( $text, 3, $wgDebugLogFile );
172 }
173 }
174
175 /**
176 * Send a line to a supplementary debug log file, if configured, or main debug log if not.
177 * $wgDebugLogGroups[$logGroup] should be set to a filename to send to a separate log.
178 *
179 * @param $logGroup String
180 * @param $text String
181 * @param $public Bool: whether to log the event in the public log if no private
182 * log file is specified, (default true)
183 */
184 function wfDebugLog( $logGroup, $text, $public = true ) {
185 global $wgDebugLogGroups;
186 if( $text{strlen( $text ) - 1} != "\n" ) $text .= "\n";
187 if( isset( $wgDebugLogGroups[$logGroup] ) ) {
188 $time = wfTimestamp( TS_DB );
189 $wiki = wfWikiID();
190 @error_log( "$time $wiki: $text", 3, $wgDebugLogGroups[$logGroup] );
191 } else if ( $public === true ) {
192 wfDebug( $text, true );
193 }
194 }
195
196 /**
197 * Log for database errors
198 * @param $text String: database error message.
199 */
200 function wfLogDBError( $text ) {
201 global $wgDBerrorLog;
202 if ( $wgDBerrorLog ) {
203 $host = trim(`hostname`);
204 $text = date('D M j G:i:s T Y') . "\t$host\t".$text;
205 error_log( $text, 3, $wgDBerrorLog );
206 }
207 }
208
209 /**
210 * @todo document
211 */
212 function wfLogProfilingData() {
213 global $wgRequestTime, $wgDebugLogFile, $wgDebugRawPage, $wgRequest;
214 global $wgProfiling, $wgUser;
215 if ( $wgProfiling ) {
216 $now = wfTime();
217 $elapsed = $now - $wgRequestTime;
218 $prof = wfGetProfilingOutput( $wgRequestTime, $elapsed );
219 $forward = '';
220 if( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) )
221 $forward = ' forwarded for ' . $_SERVER['HTTP_X_FORWARDED_FOR'];
222 if( !empty( $_SERVER['HTTP_CLIENT_IP'] ) )
223 $forward .= ' client IP ' . $_SERVER['HTTP_CLIENT_IP'];
224 if( !empty( $_SERVER['HTTP_FROM'] ) )
225 $forward .= ' from ' . $_SERVER['HTTP_FROM'];
226 if( $forward )
227 $forward = "\t(proxied via {$_SERVER['REMOTE_ADDR']}{$forward})";
228 // Don't unstub $wgUser at this late stage just for statistics purposes
229 if( StubObject::isRealObject($wgUser) && $wgUser->isAnon() )
230 $forward .= ' anon';
231 $log = sprintf( "%s\t%04.3f\t%s\n",
232 gmdate( 'YmdHis' ), $elapsed,
233 urldecode( $wgRequest->getRequestURL() . $forward ) );
234 if ( '' != $wgDebugLogFile && ( $wgRequest->getVal('action') != 'raw' || $wgDebugRawPage ) ) {
235 error_log( $log . $prof, 3, $wgDebugLogFile );
236 }
237 }
238 }
239
240 /**
241 * Check if the wiki read-only lock file is present. This can be used to lock
242 * off editing functions, but doesn't guarantee that the database will not be
243 * modified.
244 * @return bool
245 */
246 function wfReadOnly() {
247 global $wgReadOnlyFile, $wgReadOnly;
248
249 if ( !is_null( $wgReadOnly ) ) {
250 return (bool)$wgReadOnly;
251 }
252 if ( '' == $wgReadOnlyFile ) {
253 return false;
254 }
255 // Set $wgReadOnly for faster access next time
256 if ( is_file( $wgReadOnlyFile ) ) {
257 $wgReadOnly = file_get_contents( $wgReadOnlyFile );
258 } else {
259 $wgReadOnly = false;
260 }
261 return (bool)$wgReadOnly;
262 }
263
264
265 /**
266 * Get a message from anywhere, for the current user language.
267 *
268 * Use wfMsgForContent() instead if the message should NOT
269 * change depending on the user preferences.
270 *
271 * Note that the message may contain HTML, and is therefore
272 * not safe for insertion anywhere. Some functions such as
273 * addWikiText will do the escaping for you. Use wfMsgHtml()
274 * if you need an escaped message.
275 *
276 * @param $key String: lookup key for the message, usually
277 * defined in languages/Language.php
278 *
279 * This function also takes extra optional parameters (not
280 * shown in the function definition), which can by used to
281 * insert variable text into the predefined message.
282 */
283 function wfMsg( $key ) {
284 $args = func_get_args();
285 array_shift( $args );
286 return wfMsgReal( $key, $args, true );
287 }
288
289 /**
290 * Same as above except doesn't transform the message
291 */
292 function wfMsgNoTrans( $key ) {
293 $args = func_get_args();
294 array_shift( $args );
295 return wfMsgReal( $key, $args, true, false, false );
296 }
297
298 /**
299 * Get a message from anywhere, for the current global language
300 * set with $wgLanguageCode.
301 *
302 * Use this if the message should NOT change dependent on the
303 * language set in the user's preferences. This is the case for
304 * most text written into logs, as well as link targets (such as
305 * the name of the copyright policy page). Link titles, on the
306 * other hand, should be shown in the UI language.
307 *
308 * Note that MediaWiki allows users to change the user interface
309 * language in their preferences, but a single installation
310 * typically only contains content in one language.
311 *
312 * Be wary of this distinction: If you use wfMsg() where you should
313 * use wfMsgForContent(), a user of the software may have to
314 * customize over 70 messages in order to, e.g., fix a link in every
315 * possible language.
316 *
317 * @param $key String: lookup key for the message, usually
318 * defined in languages/Language.php
319 */
320 function wfMsgForContent( $key ) {
321 global $wgForceUIMsgAsContentMsg;
322 $args = func_get_args();
323 array_shift( $args );
324 $forcontent = true;
325 if( is_array( $wgForceUIMsgAsContentMsg ) &&
326 in_array( $key, $wgForceUIMsgAsContentMsg ) )
327 $forcontent = false;
328 return wfMsgReal( $key, $args, true, $forcontent );
329 }
330
331 /**
332 * Same as above except doesn't transform the message
333 */
334 function wfMsgForContentNoTrans( $key ) {
335 global $wgForceUIMsgAsContentMsg;
336 $args = func_get_args();
337 array_shift( $args );
338 $forcontent = true;
339 if( is_array( $wgForceUIMsgAsContentMsg ) &&
340 in_array( $key, $wgForceUIMsgAsContentMsg ) )
341 $forcontent = false;
342 return wfMsgReal( $key, $args, true, $forcontent, false );
343 }
344
345 /**
346 * Get a message from the language file, for the UI elements
347 */
348 function wfMsgNoDB( $key ) {
349 $args = func_get_args();
350 array_shift( $args );
351 return wfMsgReal( $key, $args, false );
352 }
353
354 /**
355 * Get a message from the language file, for the content
356 */
357 function wfMsgNoDBForContent( $key ) {
358 global $wgForceUIMsgAsContentMsg;
359 $args = func_get_args();
360 array_shift( $args );
361 $forcontent = true;
362 if( is_array( $wgForceUIMsgAsContentMsg ) &&
363 in_array( $key, $wgForceUIMsgAsContentMsg ) )
364 $forcontent = false;
365 return wfMsgReal( $key, $args, false, $forcontent );
366 }
367
368
369 /**
370 * Really get a message
371 * @param $key String: key to get.
372 * @param $args
373 * @param $useDB Boolean
374 * @param $transform Boolean: Whether or not to transform the message.
375 * @param $forContent Boolean
376 * @return String: the requested message.
377 */
378 function wfMsgReal( $key, $args, $useDB = true, $forContent=false, $transform = true ) {
379 $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
380 $message = wfMsgReplaceArgs( $message, $args );
381 return $message;
382 }
383
384 /**
385 * This function provides the message source for messages to be edited which are *not* stored in the database.
386 * @param $key String:
387 */
388 function wfMsgWeirdKey ( $key ) {
389 $subsource = str_replace ( ' ' , '_' , $key ) ;
390 $source = wfMsgForContentNoTrans( $subsource ) ;
391 if ( wfEmptyMsg( $subsource, $source) ) {
392 # Try again with first char lower case
393 $subsource = strtolower ( substr ( $subsource , 0 , 1 ) ) . substr ( $subsource , 1 ) ;
394 $source = wfMsgForContentNoTrans( $subsource ) ;
395 }
396 if ( wfEmptyMsg( $subsource, $source ) ) {
397 # Didn't work either, return blank text
398 $source = "" ;
399 }
400 return $source ;
401 }
402
403 /**
404 * Fetch a message string value, but don't replace any keys yet.
405 * @param string $key
406 * @param bool $useDB
407 * @param bool $forContent
408 * @return string
409 * @private
410 */
411 function wfMsgGetKey( $key, $useDB, $forContent = false, $transform = true ) {
412 global $wgParser, $wgContLang, $wgMessageCache, $wgLang;
413
414 if ( is_object( $wgMessageCache ) )
415 $transstat = $wgMessageCache->getTransform();
416
417 if( is_object( $wgMessageCache ) ) {
418 if ( ! $transform )
419 $wgMessageCache->disableTransform();
420 $message = $wgMessageCache->get( $key, $useDB, $forContent );
421 } else {
422 if( $forContent ) {
423 $lang = &$wgContLang;
424 } else {
425 $lang = &$wgLang;
426 }
427
428 wfSuppressWarnings();
429
430 if( is_object( $lang ) ) {
431 $message = $lang->getMessage( $key );
432 } else {
433 $message = false;
434 }
435 wfRestoreWarnings();
436 if($message === false)
437 $message = Language::getMessage($key);
438 if ( $transform && strstr( $message, '{{' ) !== false ) {
439 $message = $wgParser->transformMsg($message, $wgMessageCache->getParserOptions() );
440 }
441 }
442
443 if ( is_object( $wgMessageCache ) && ! $transform )
444 $wgMessageCache->setTransform( $transstat );
445
446 return $message;
447 }
448
449 /**
450 * Replace message parameter keys on the given formatted output.
451 *
452 * @param string $message
453 * @param array $args
454 * @return string
455 * @private
456 */
457 function wfMsgReplaceArgs( $message, $args ) {
458 # Fix windows line-endings
459 # Some messages are split with explode("\n", $msg)
460 $message = str_replace( "\r", '', $message );
461
462 // Replace arguments
463 if ( count( $args ) ) {
464 if ( is_array( $args[0] ) ) {
465 foreach ( $args[0] as $key => $val ) {
466 $message = str_replace( '$' . $key, $val, $message );
467 }
468 } else {
469 foreach( $args as $n => $param ) {
470 $replacementKeys['$' . ($n + 1)] = $param;
471 }
472 $message = strtr( $message, $replacementKeys );
473 }
474 }
475
476 return $message;
477 }
478
479 /**
480 * Return an HTML-escaped version of a message.
481 * Parameter replacements, if any, are done *after* the HTML-escaping,
482 * so parameters may contain HTML (eg links or form controls). Be sure
483 * to pre-escape them if you really do want plaintext, or just wrap
484 * the whole thing in htmlspecialchars().
485 *
486 * @param string $key
487 * @param string ... parameters
488 * @return string
489 */
490 function wfMsgHtml( $key ) {
491 $args = func_get_args();
492 array_shift( $args );
493 return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key, true ) ), $args );
494 }
495
496 /**
497 * Return an HTML version of message
498 * Parameter replacements, if any, are done *after* parsing the wiki-text message,
499 * so parameters may contain HTML (eg links or form controls). Be sure
500 * to pre-escape them if you really do want plaintext, or just wrap
501 * the whole thing in htmlspecialchars().
502 *
503 * @param string $key
504 * @param string ... parameters
505 * @return string
506 */
507 function wfMsgWikiHtml( $key ) {
508 global $wgOut;
509 $args = func_get_args();
510 array_shift( $args );
511 return wfMsgReplaceArgs( $wgOut->parse( wfMsgGetKey( $key, true ), /* can't be set to false */ true ), $args );
512 }
513
514 /**
515 * Returns message in the requested format
516 * @param string $key Key of the message
517 * @param array $options Processing rules:
518 * <i>parse<i>: parses wikitext to html
519 * <i>parseinline<i>: parses wikitext to html and removes the surrounding p's added by parser or tidy
520 * <i>escape<i>: filters message trough htmlspecialchars
521 * <i>replaceafter<i>: parameters are substituted after parsing or escaping
522 * <i>parsemag<i>: ??
523 */
524 function wfMsgExt( $key, $options ) {
525 global $wgOut, $wgParser;
526
527 $args = func_get_args();
528 array_shift( $args );
529 array_shift( $args );
530
531 if( !is_array($options) ) {
532 $options = array($options);
533 }
534
535 $string = wfMsgGetKey( $key, true, false, false );
536
537 if( !in_array('replaceafter', $options) ) {
538 $string = wfMsgReplaceArgs( $string, $args );
539 }
540
541 if( in_array('parse', $options) ) {
542 $string = $wgOut->parse( $string, true, true );
543 } elseif ( in_array('parseinline', $options) ) {
544 $string = $wgOut->parse( $string, true, true );
545 $m = array();
546 if( preg_match( "~^<p>(.*)\n?</p>$~", $string, $m ) ) {
547 $string = $m[1];
548 }
549 } elseif ( in_array('parsemag', $options) ) {
550 global $wgMessageCache;
551 if ( isset( $wgMessageCache ) ) {
552 $string = $wgMessageCache->transform( $string );
553 }
554 }
555
556 if ( in_array('escape', $options) ) {
557 $string = htmlspecialchars ( $string );
558 }
559
560 if( in_array('replaceafter', $options) ) {
561 $string = wfMsgReplaceArgs( $string, $args );
562 }
563
564 return $string;
565 }
566
567
568 /**
569 * Just like exit() but makes a note of it.
570 * Commits open transactions except if the error parameter is set
571 *
572 * @obsolete Please return control to the caller or throw an exception
573 */
574 function wfAbruptExit( $error = false ){
575 global $wgLoadBalancer;
576 static $called = false;
577 if ( $called ){
578 exit( -1 );
579 }
580 $called = true;
581
582 $bt = wfDebugBacktrace();
583 if( $bt ) {
584 for($i = 0; $i < count($bt) ; $i++){
585 $file = isset($bt[$i]['file']) ? $bt[$i]['file'] : "unknown";
586 $line = isset($bt[$i]['line']) ? $bt[$i]['line'] : "unknown";
587 wfDebug("WARNING: Abrupt exit in $file at line $line\n");
588 }
589 } else {
590 wfDebug('WARNING: Abrupt exit\n');
591 }
592
593 wfLogProfilingData();
594
595 if ( !$error ) {
596 $wgLoadBalancer->closeAll();
597 }
598 exit( -1 );
599 }
600
601 /**
602 * @obsolete Please return control the caller or throw an exception
603 */
604 function wfErrorExit() {
605 wfAbruptExit( true );
606 }
607
608 /**
609 * Print a simple message and die, returning nonzero to the shell if any.
610 * Plain die() fails to return nonzero to the shell if you pass a string.
611 * @param string $msg
612 */
613 function wfDie( $msg='' ) {
614 echo $msg;
615 die( 1 );
616 }
617
618 /**
619 * Throw a debugging exception. This function previously once exited the process,
620 * but now throws an exception instead, with similar results.
621 *
622 * @param string $msg Message shown when dieing.
623 */
624 function wfDebugDieBacktrace( $msg = '' ) {
625 throw new MWException( $msg );
626 }
627
628 /**
629 * Fetch server name for use in error reporting etc.
630 * Use real server name if available, so we know which machine
631 * in a server farm generated the current page.
632 * @return string
633 */
634 function wfHostname() {
635 if ( function_exists( 'posix_uname' ) ) {
636 // This function not present on Windows
637 $uname = @posix_uname();
638 } else {
639 $uname = false;
640 }
641 if( is_array( $uname ) && isset( $uname['nodename'] ) ) {
642 return $uname['nodename'];
643 } else {
644 # This may be a virtual server.
645 return $_SERVER['SERVER_NAME'];
646 }
647 }
648
649 /**
650 * Returns a HTML comment with the elapsed time since request.
651 * This method has no side effects.
652 * @return string
653 */
654 function wfReportTime() {
655 global $wgRequestTime;
656
657 $now = wfTime();
658 $elapsed = $now - $wgRequestTime;
659
660 $com = sprintf( "<!-- Served by %s in %01.3f secs. -->",
661 wfHostname(), $elapsed );
662 return $com;
663 }
664
665 /**
666 * Safety wrapper for debug_backtrace().
667 *
668 * With Zend Optimizer 3.2.0 loaded, this causes segfaults under somewhat
669 * murky circumstances, which may be triggered in part by stub objects
670 * or other fancy talkin'.
671 *
672 * Will return an empty array if Zend Optimizer is detected, otherwise
673 * the output from debug_backtrace() (trimmed).
674 *
675 * @return array of backtrace information
676 */
677 function wfDebugBacktrace() {
678 if( extension_loaded( 'Zend Optimizer' ) ) {
679 wfDebug( "Zend Optimizer detected; skipping debug_backtrace for safety.\n" );
680 return array();
681 } else {
682 return array_slice( debug_backtrace(), 1 );
683 }
684 }
685
686 function wfBacktrace() {
687 global $wgCommandLineMode;
688
689 if ( $wgCommandLineMode ) {
690 $msg = '';
691 } else {
692 $msg = "<ul>\n";
693 }
694 $backtrace = wfDebugBacktrace();
695 foreach( $backtrace as $call ) {
696 if( isset( $call['file'] ) ) {
697 $f = explode( DIRECTORY_SEPARATOR, $call['file'] );
698 $file = $f[count($f)-1];
699 } else {
700 $file = '-';
701 }
702 if( isset( $call['line'] ) ) {
703 $line = $call['line'];
704 } else {
705 $line = '-';
706 }
707 if ( $wgCommandLineMode ) {
708 $msg .= "$file line $line calls ";
709 } else {
710 $msg .= '<li>' . $file . ' line ' . $line . ' calls ';
711 }
712 if( !empty( $call['class'] ) ) $msg .= $call['class'] . '::';
713 $msg .= $call['function'] . '()';
714
715 if ( $wgCommandLineMode ) {
716 $msg .= "\n";
717 } else {
718 $msg .= "</li>\n";
719 }
720 }
721 if ( $wgCommandLineMode ) {
722 $msg .= "\n";
723 } else {
724 $msg .= "</ul>\n";
725 }
726
727 return $msg;
728 }
729
730
731 /* Some generic result counters, pulled out of SearchEngine */
732
733
734 /**
735 * @todo document
736 */
737 function wfShowingResults( $offset, $limit ) {
738 global $wgLang;
739 return wfMsg( 'showingresults', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ) );
740 }
741
742 /**
743 * @todo document
744 */
745 function wfShowingResultsNum( $offset, $limit, $num ) {
746 global $wgLang;
747 return wfMsg( 'showingresultsnum', $wgLang->formatNum( $limit ), $wgLang->formatNum( $offset+1 ), $wgLang->formatNum( $num ) );
748 }
749
750 /**
751 * @todo document
752 */
753 function wfViewPrevNext( $offset, $limit, $link, $query = '', $atend = false ) {
754 global $wgLang;
755 $fmtLimit = $wgLang->formatNum( $limit );
756 $prev = wfMsg( 'prevn', $fmtLimit );
757 $next = wfMsg( 'nextn', $fmtLimit );
758
759 if( is_object( $link ) ) {
760 $title =& $link;
761 } else {
762 $title = Title::newFromText( $link );
763 if( is_null( $title ) ) {
764 return false;
765 }
766 }
767
768 if ( 0 != $offset ) {
769 $po = $offset - $limit;
770 if ( $po < 0 ) { $po = 0; }
771 $q = "limit={$limit}&offset={$po}";
772 if ( '' != $query ) { $q .= '&'.$query; }
773 $plink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$prev}</a>";
774 } else { $plink = $prev; }
775
776 $no = $offset + $limit;
777 $q = 'limit='.$limit.'&offset='.$no;
778 if ( '' != $query ) { $q .= '&'.$query; }
779
780 if ( $atend ) {
781 $nlink = $next;
782 } else {
783 $nlink = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$next}</a>";
784 }
785 $nums = wfNumLink( $offset, 20, $title, $query ) . ' | ' .
786 wfNumLink( $offset, 50, $title, $query ) . ' | ' .
787 wfNumLink( $offset, 100, $title, $query ) . ' | ' .
788 wfNumLink( $offset, 250, $title, $query ) . ' | ' .
789 wfNumLink( $offset, 500, $title, $query );
790
791 return wfMsg( 'viewprevnext', $plink, $nlink, $nums );
792 }
793
794 /**
795 * @todo document
796 */
797 function wfNumLink( $offset, $limit, &$title, $query = '' ) {
798 global $wgLang;
799 if ( '' == $query ) { $q = ''; }
800 else { $q = $query.'&'; }
801 $q .= 'limit='.$limit.'&offset='.$offset;
802
803 $fmtLimit = $wgLang->formatNum( $limit );
804 $s = '<a href="' . $title->escapeLocalUrl( $q ) . "\">{$fmtLimit}</a>";
805 return $s;
806 }
807
808 /**
809 * @todo document
810 * @todo FIXME: we may want to blacklist some broken browsers
811 *
812 * @return bool Whereas client accept gzip compression
813 */
814 function wfClientAcceptsGzip() {
815 global $wgUseGzip;
816 if( $wgUseGzip ) {
817 # FIXME: we may want to blacklist some broken browsers
818 $m = array();
819 if( preg_match(
820 '/\bgzip(?:;(q)=([0-9]+(?:\.[0-9]+)))?\b/',
821 $_SERVER['HTTP_ACCEPT_ENCODING'],
822 $m ) ) {
823 if( isset( $m[2] ) && ( $m[1] == 'q' ) && ( $m[2] == 0 ) ) return false;
824 wfDebug( " accepts gzip\n" );
825 return true;
826 }
827 }
828 return false;
829 }
830
831 /**
832 * Obtain the offset and limit values from the request string;
833 * used in special pages
834 *
835 * @param $deflimit Default limit if none supplied
836 * @param $optionname Name of a user preference to check against
837 * @return array
838 *
839 */
840 function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
841 global $wgRequest;
842 return $wgRequest->getLimitOffset( $deflimit, $optionname );
843 }
844
845 /**
846 * Escapes the given text so that it may be output using addWikiText()
847 * without any linking, formatting, etc. making its way through. This
848 * is achieved by substituting certain characters with HTML entities.
849 * As required by the callers, <nowiki> is not used. It currently does
850 * not filter out characters which have special meaning only at the
851 * start of a line, such as "*".
852 *
853 * @param string $text Text to be escaped
854 */
855 function wfEscapeWikiText( $text ) {
856 $text = str_replace(
857 array( '[', '|', '\'', 'ISBN ', 'RFC ', '://', "\n=", '{{' ),
858 array( '&#91;', '&#124;', '&#39;', 'ISBN&#32;', 'RFC&#32;', '&#58;//', "\n&#61;", '&#123;&#123;' ),
859 htmlspecialchars($text) );
860 return $text;
861 }
862
863 /**
864 * @todo document
865 */
866 function wfQuotedPrintable( $string, $charset = '' ) {
867 # Probably incomplete; see RFC 2045
868 if( empty( $charset ) ) {
869 global $wgInputEncoding;
870 $charset = $wgInputEncoding;
871 }
872 $charset = strtoupper( $charset );
873 $charset = str_replace( 'ISO-8859', 'ISO8859', $charset ); // ?
874
875 $illegal = '\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff=';
876 $replace = $illegal . '\t ?_';
877 if( !preg_match( "/[$illegal]/", $string ) ) return $string;
878 $out = "=?$charset?Q?";
879 $out .= preg_replace( "/([$replace])/e", 'sprintf("=%02X",ord("$1"))', $string );
880 $out .= '?=';
881 return $out;
882 }
883
884
885 /**
886 * @todo document
887 * @return float
888 */
889 function wfTime() {
890 return microtime(true);
891 }
892
893 /**
894 * Sets dest to source and returns the original value of dest
895 * If source is NULL, it just returns the value, it doesn't set the variable
896 */
897 function wfSetVar( &$dest, $source ) {
898 $temp = $dest;
899 if ( !is_null( $source ) ) {
900 $dest = $source;
901 }
902 return $temp;
903 }
904
905 /**
906 * As for wfSetVar except setting a bit
907 */
908 function wfSetBit( &$dest, $bit, $state = true ) {
909 $temp = (bool)($dest & $bit );
910 if ( !is_null( $state ) ) {
911 if ( $state ) {
912 $dest |= $bit;
913 } else {
914 $dest &= ~$bit;
915 }
916 }
917 return $temp;
918 }
919
920 /**
921 * This function takes two arrays as input, and returns a CGI-style string, e.g.
922 * "days=7&limit=100". Options in the first array override options in the second.
923 * Options set to "" will not be output.
924 */
925 function wfArrayToCGI( $array1, $array2 = NULL )
926 {
927 if ( !is_null( $array2 ) ) {
928 $array1 = $array1 + $array2;
929 }
930
931 $cgi = '';
932 foreach ( $array1 as $key => $value ) {
933 if ( '' !== $value ) {
934 if ( '' != $cgi ) {
935 $cgi .= '&';
936 }
937 $cgi .= urlencode( $key ) . '=' . urlencode( $value );
938 }
939 }
940 return $cgi;
941 }
942
943 /**
944 * This is obsolete, use SquidUpdate::purge()
945 * @deprecated
946 */
947 function wfPurgeSquidServers ($urlArr) {
948 SquidUpdate::purge( $urlArr );
949 }
950
951 /**
952 * Windows-compatible version of escapeshellarg()
953 * Windows doesn't recognise single-quotes in the shell, but the escapeshellarg()
954 * function puts single quotes in regardless of OS
955 */
956 function wfEscapeShellArg( ) {
957 $args = func_get_args();
958 $first = true;
959 $retVal = '';
960 foreach ( $args as $arg ) {
961 if ( !$first ) {
962 $retVal .= ' ';
963 } else {
964 $first = false;
965 }
966
967 if ( wfIsWindows() ) {
968 // Escaping for an MSVC-style command line parser
969 // Ref: http://mailman.lyra.org/pipermail/scite-interest/2002-March/000436.html
970 // Double the backslashes before any double quotes. Escape the double quotes.
971 $tokens = preg_split( '/(\\\\*")/', $arg, -1, PREG_SPLIT_DELIM_CAPTURE );
972 $arg = '';
973 $delim = false;
974 foreach ( $tokens as $token ) {
975 if ( $delim ) {
976 $arg .= str_replace( '\\', '\\\\', substr( $token, 0, -1 ) ) . '\\"';
977 } else {
978 $arg .= $token;
979 }
980 $delim = !$delim;
981 }
982 // Double the backslashes before the end of the string, because
983 // we will soon add a quote
984 $m = array();
985 if ( preg_match( '/^(.*?)(\\\\+)$/', $arg, $m ) ) {
986 $arg = $m[1] . str_replace( '\\', '\\\\', $m[2] );
987 }
988
989 // Add surrounding quotes
990 $retVal .= '"' . $arg . '"';
991 } else {
992 $retVal .= escapeshellarg( $arg );
993 }
994 }
995 return $retVal;
996 }
997
998 /**
999 * wfMerge attempts to merge differences between three texts.
1000 * Returns true for a clean merge and false for failure or a conflict.
1001 */
1002 function wfMerge( $old, $mine, $yours, &$result ){
1003 global $wgDiff3;
1004
1005 # This check may also protect against code injection in
1006 # case of broken installations.
1007 if(! file_exists( $wgDiff3 ) ){
1008 wfDebug( "diff3 not found\n" );
1009 return false;
1010 }
1011
1012 # Make temporary files
1013 $td = wfTempDir();
1014 $oldtextFile = fopen( $oldtextName = tempnam( $td, 'merge-old-' ), 'w' );
1015 $mytextFile = fopen( $mytextName = tempnam( $td, 'merge-mine-' ), 'w' );
1016 $yourtextFile = fopen( $yourtextName = tempnam( $td, 'merge-your-' ), 'w' );
1017
1018 fwrite( $oldtextFile, $old ); fclose( $oldtextFile );
1019 fwrite( $mytextFile, $mine ); fclose( $mytextFile );
1020 fwrite( $yourtextFile, $yours ); fclose( $yourtextFile );
1021
1022 # Check for a conflict
1023 $cmd = $wgDiff3 . ' -a --overlap-only ' .
1024 wfEscapeShellArg( $mytextName ) . ' ' .
1025 wfEscapeShellArg( $oldtextName ) . ' ' .
1026 wfEscapeShellArg( $yourtextName );
1027 $handle = popen( $cmd, 'r' );
1028
1029 if( fgets( $handle, 1024 ) ){
1030 $conflict = true;
1031 } else {
1032 $conflict = false;
1033 }
1034 pclose( $handle );
1035
1036 # Merge differences
1037 $cmd = $wgDiff3 . ' -a -e --merge ' .
1038 wfEscapeShellArg( $mytextName, $oldtextName, $yourtextName );
1039 $handle = popen( $cmd, 'r' );
1040 $result = '';
1041 do {
1042 $data = fread( $handle, 8192 );
1043 if ( strlen( $data ) == 0 ) {
1044 break;
1045 }
1046 $result .= $data;
1047 } while ( true );
1048 pclose( $handle );
1049 unlink( $mytextName ); unlink( $oldtextName ); unlink( $yourtextName );
1050
1051 if ( $result === '' && $old !== '' && $conflict == false ) {
1052 wfDebug( "Unexpected null result from diff3. Command: $cmd\n" );
1053 $conflict = true;
1054 }
1055 return ! $conflict;
1056 }
1057
1058 /**
1059 * @todo document
1060 */
1061 function wfVarDump( $var ) {
1062 global $wgOut;
1063 $s = str_replace("\n","<br />\n", var_export( $var, true ) . "\n");
1064 if ( headers_sent() || !@is_object( $wgOut ) ) {
1065 print $s;
1066 } else {
1067 $wgOut->addHTML( $s );
1068 }
1069 }
1070
1071 /**
1072 * Provide a simple HTTP error.
1073 */
1074 function wfHttpError( $code, $label, $desc ) {
1075 global $wgOut;
1076 $wgOut->disable();
1077 header( "HTTP/1.0 $code $label" );
1078 header( "Status: $code $label" );
1079 $wgOut->sendCacheControl();
1080
1081 header( 'Content-type: text/html' );
1082 print "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">".
1083 "<html><head><title>" .
1084 htmlspecialchars( $label ) .
1085 "</title></head><body><h1>" .
1086 htmlspecialchars( $label ) .
1087 "</h1><p>" .
1088 nl2br( htmlspecialchars( $desc ) ) .
1089 "</p></body></html>\n";
1090 }
1091
1092 /**
1093 * Clear away any user-level output buffers, discarding contents.
1094 *
1095 * Suitable for 'starting afresh', for instance when streaming
1096 * relatively large amounts of data without buffering, or wanting to
1097 * output image files without ob_gzhandler's compression.
1098 *
1099 * The optional $resetGzipEncoding parameter controls suppression of
1100 * the Content-Encoding header sent by ob_gzhandler; by default it
1101 * is left. See comments for wfClearOutputBuffers() for why it would
1102 * be used.
1103 *
1104 * Note that some PHP configuration options may add output buffer
1105 * layers which cannot be removed; these are left in place.
1106 *
1107 * @parameter bool $resetGzipEncoding
1108 */
1109 function wfResetOutputBuffers( $resetGzipEncoding=true ) {
1110 while( $status = ob_get_status() ) {
1111 if( $status['type'] == 0 /* PHP_OUTPUT_HANDLER_INTERNAL */ ) {
1112 // Probably from zlib.output_compression or other
1113 // PHP-internal setting which can't be removed.
1114 //
1115 // Give up, and hope the result doesn't break
1116 // output behavior.
1117 break;
1118 }
1119 if( !ob_end_clean() ) {
1120 // Could not remove output buffer handler; abort now
1121 // to avoid getting in some kind of infinite loop.
1122 break;
1123 }
1124 if( $resetGzipEncoding ) {
1125 if( $status['name'] == 'ob_gzhandler' ) {
1126 // Reset the 'Content-Encoding' field set by this handler
1127 // so we can start fresh.
1128 header( 'Content-Encoding:' );
1129 }
1130 }
1131 }
1132 }
1133
1134 /**
1135 * More legible than passing a 'false' parameter to wfResetOutputBuffers():
1136 *
1137 * Clear away output buffers, but keep the Content-Encoding header
1138 * produced by ob_gzhandler, if any.
1139 *
1140 * This should be used for HTTP 304 responses, where you need to
1141 * preserve the Content-Encoding header of the real result, but
1142 * also need to suppress the output of ob_gzhandler to keep to spec
1143 * and avoid breaking Firefox in rare cases where the headers and
1144 * body are broken over two packets.
1145 */
1146 function wfClearOutputBuffers() {
1147 wfResetOutputBuffers( false );
1148 }
1149
1150 /**
1151 * Converts an Accept-* header into an array mapping string values to quality
1152 * factors
1153 */
1154 function wfAcceptToPrefs( $accept, $def = '*/*' ) {
1155 # No arg means accept anything (per HTTP spec)
1156 if( !$accept ) {
1157 return array( $def => 1 );
1158 }
1159
1160 $prefs = array();
1161
1162 $parts = explode( ',', $accept );
1163
1164 foreach( $parts as $part ) {
1165 # FIXME: doesn't deal with params like 'text/html; level=1'
1166 @list( $value, $qpart ) = explode( ';', $part );
1167 $match = array();
1168 if( !isset( $qpart ) ) {
1169 $prefs[$value] = 1;
1170 } elseif( preg_match( '/q\s*=\s*(\d*\.\d+)/', $qpart, $match ) ) {
1171 $prefs[$value] = $match[1];
1172 }
1173 }
1174
1175 return $prefs;
1176 }
1177
1178 /**
1179 * Checks if a given MIME type matches any of the keys in the given
1180 * array. Basic wildcards are accepted in the array keys.
1181 *
1182 * Returns the matching MIME type (or wildcard) if a match, otherwise
1183 * NULL if no match.
1184 *
1185 * @param string $type
1186 * @param array $avail
1187 * @return string
1188 * @private
1189 */
1190 function mimeTypeMatch( $type, $avail ) {
1191 if( array_key_exists($type, $avail) ) {
1192 return $type;
1193 } else {
1194 $parts = explode( '/', $type );
1195 if( array_key_exists( $parts[0] . '/*', $avail ) ) {
1196 return $parts[0] . '/*';
1197 } elseif( array_key_exists( '*/*', $avail ) ) {
1198 return '*/*';
1199 } else {
1200 return NULL;
1201 }
1202 }
1203 }
1204
1205 /**
1206 * Returns the 'best' match between a client's requested internet media types
1207 * and the server's list of available types. Each list should be an associative
1208 * array of type to preference (preference is a float between 0.0 and 1.0).
1209 * Wildcards in the types are acceptable.
1210 *
1211 * @param array $cprefs Client's acceptable type list
1212 * @param array $sprefs Server's offered types
1213 * @return string
1214 *
1215 * @todo FIXME: doesn't handle params like 'text/plain; charset=UTF-8'
1216 * XXX: generalize to negotiate other stuff
1217 */
1218 function wfNegotiateType( $cprefs, $sprefs ) {
1219 $combine = array();
1220
1221 foreach( array_keys($sprefs) as $type ) {
1222 $parts = explode( '/', $type );
1223 if( $parts[1] != '*' ) {
1224 $ckey = mimeTypeMatch( $type, $cprefs );
1225 if( $ckey ) {
1226 $combine[$type] = $sprefs[$type] * $cprefs[$ckey];
1227 }
1228 }
1229 }
1230
1231 foreach( array_keys( $cprefs ) as $type ) {
1232 $parts = explode( '/', $type );
1233 if( $parts[1] != '*' && !array_key_exists( $type, $sprefs ) ) {
1234 $skey = mimeTypeMatch( $type, $sprefs );
1235 if( $skey ) {
1236 $combine[$type] = $sprefs[$skey] * $cprefs[$type];
1237 }
1238 }
1239 }
1240
1241 $bestq = 0;
1242 $besttype = NULL;
1243
1244 foreach( array_keys( $combine ) as $type ) {
1245 if( $combine[$type] > $bestq ) {
1246 $besttype = $type;
1247 $bestq = $combine[$type];
1248 }
1249 }
1250
1251 return $besttype;
1252 }
1253
1254 /**
1255 * Array lookup
1256 * Returns an array where the values in the first array are replaced by the
1257 * values in the second array with the corresponding keys
1258 *
1259 * @return array
1260 */
1261 function wfArrayLookup( $a, $b ) {
1262 return array_flip( array_intersect( array_flip( $a ), array_keys( $b ) ) );
1263 }
1264
1265 /**
1266 * Convenience function; returns MediaWiki timestamp for the present time.
1267 * @return string
1268 */
1269 function wfTimestampNow() {
1270 # return NOW
1271 return wfTimestamp( TS_MW, time() );
1272 }
1273
1274 /**
1275 * Reference-counted warning suppression
1276 */
1277 function wfSuppressWarnings( $end = false ) {
1278 static $suppressCount = 0;
1279 static $originalLevel = false;
1280
1281 if ( $end ) {
1282 if ( $suppressCount ) {
1283 --$suppressCount;
1284 if ( !$suppressCount ) {
1285 error_reporting( $originalLevel );
1286 }
1287 }
1288 } else {
1289 if ( !$suppressCount ) {
1290 $originalLevel = error_reporting( E_ALL & ~( E_WARNING | E_NOTICE ) );
1291 }
1292 ++$suppressCount;
1293 }
1294 }
1295
1296 /**
1297 * Restore error level to previous value
1298 */
1299 function wfRestoreWarnings() {
1300 wfSuppressWarnings( true );
1301 }
1302
1303 # Autodetect, convert and provide timestamps of various types
1304
1305 /**
1306 * Unix time - the number of seconds since 1970-01-01 00:00:00 UTC
1307 */
1308 define('TS_UNIX', 0);
1309
1310 /**
1311 * MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
1312 */
1313 define('TS_MW', 1);
1314
1315 /**
1316 * MySQL DATETIME (YYYY-MM-DD HH:MM:SS)
1317 */
1318 define('TS_DB', 2);
1319
1320 /**
1321 * RFC 2822 format, for E-mail and HTTP headers
1322 */
1323 define('TS_RFC2822', 3);
1324
1325 /**
1326 * ISO 8601 format with no timezone: 1986-02-09T20:00:00Z
1327 *
1328 * This is used by Special:Export
1329 */
1330 define('TS_ISO_8601', 4);
1331
1332 /**
1333 * An Exif timestamp (YYYY:MM:DD HH:MM:SS)
1334 *
1335 * @url http://exif.org/Exif2-2.PDF The Exif 2.2 spec, see page 28 for the
1336 * DateTime tag and page 36 for the DateTimeOriginal and
1337 * DateTimeDigitized tags.
1338 */
1339 define('TS_EXIF', 5);
1340
1341 /**
1342 * Oracle format time.
1343 */
1344 define('TS_ORACLE', 6);
1345
1346 /**
1347 * Postgres format time.
1348 */
1349 define('TS_POSTGRES', 7);
1350
1351 /**
1352 * @param mixed $outputtype A timestamp in one of the supported formats, the
1353 * function will autodetect which format is supplied
1354 * and act accordingly.
1355 * @return string Time in the format specified in $outputtype
1356 */
1357 function wfTimestamp($outputtype=TS_UNIX,$ts=0) {
1358 $uts = 0;
1359 $da = array();
1360 if ($ts==0) {
1361 $uts=time();
1362 } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) {
1363 # TS_DB
1364 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1365 (int)$da[2],(int)$da[3],(int)$da[1]);
1366 } elseif (preg_match('/^(\d{4}):(\d\d):(\d\d) (\d\d):(\d\d):(\d\d)$/D',$ts,$da)) {
1367 # TS_EXIF
1368 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1369 (int)$da[2],(int)$da[3],(int)$da[1]);
1370 } elseif (preg_match('/^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/D',$ts,$da)) {
1371 # TS_MW
1372 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1373 (int)$da[2],(int)$da[3],(int)$da[1]);
1374 } elseif (preg_match('/^(\d{1,13})$/D',$ts,$da)) {
1375 # TS_UNIX
1376 $uts = $ts;
1377 } elseif (preg_match('/^(\d{1,2})-(...)-(\d\d(\d\d)?) (\d\d)\.(\d\d)\.(\d\d)/', $ts, $da)) {
1378 # TS_ORACLE
1379 $uts = strtotime(preg_replace('/(\d\d)\.(\d\d)\.(\d\d)(\.(\d+))?/', "$1:$2:$3",
1380 str_replace("+00:00", "UTC", $ts)));
1381 } elseif (preg_match('/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z$/', $ts, $da)) {
1382 # TS_ISO_8601
1383 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1384 (int)$da[2],(int)$da[3],(int)$da[1]);
1385 } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d)[\+\- ](\d\d)$/',$ts,$da)) {
1386 # TS_POSTGRES
1387 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1388 (int)$da[2],(int)$da[3],(int)$da[1]);
1389 } elseif (preg_match('/^(\d{4})\-(\d\d)\-(\d\d) (\d\d):(\d\d):(\d\d) GMT$/',$ts,$da)) {
1390 # TS_POSTGRES
1391 $uts=gmmktime((int)$da[4],(int)$da[5],(int)$da[6],
1392 (int)$da[2],(int)$da[3],(int)$da[1]);
1393 } else {
1394 # Bogus value; fall back to the epoch...
1395 wfDebug("wfTimestamp() fed bogus time value: $outputtype; $ts\n");
1396 $uts = 0;
1397 }
1398
1399
1400 switch($outputtype) {
1401 case TS_UNIX:
1402 return $uts;
1403 case TS_MW:
1404 return gmdate( 'YmdHis', $uts );
1405 case TS_DB:
1406 return gmdate( 'Y-m-d H:i:s', $uts );
1407 case TS_ISO_8601:
1408 return gmdate( 'Y-m-d\TH:i:s\Z', $uts );
1409 // This shouldn't ever be used, but is included for completeness
1410 case TS_EXIF:
1411 return gmdate( 'Y:m:d H:i:s', $uts );
1412 case TS_RFC2822:
1413 return gmdate( 'D, d M Y H:i:s', $uts ) . ' GMT';
1414 case TS_ORACLE:
1415 return gmdate( 'd-M-y h.i.s A', $uts) . ' +00:00';
1416 case TS_POSTGRES:
1417 return gmdate( 'Y-m-d H:i:s', $uts) . ' GMT';
1418 default:
1419 throw new MWException( 'wfTimestamp() called with illegal output type.');
1420 }
1421 }
1422
1423 /**
1424 * Return a formatted timestamp, or null if input is null.
1425 * For dealing with nullable timestamp columns in the database.
1426 * @param int $outputtype
1427 * @param string $ts
1428 * @return string
1429 */
1430 function wfTimestampOrNull( $outputtype = TS_UNIX, $ts = null ) {
1431 if( is_null( $ts ) ) {
1432 return null;
1433 } else {
1434 return wfTimestamp( $outputtype, $ts );
1435 }
1436 }
1437
1438 /**
1439 * Check if the operating system is Windows
1440 *
1441 * @return bool True if it's Windows, False otherwise.
1442 */
1443 function wfIsWindows() {
1444 if (substr(php_uname(), 0, 7) == 'Windows') {
1445 return true;
1446 } else {
1447 return false;
1448 }
1449 }
1450
1451 /**
1452 * Swap two variables
1453 */
1454 function swap( &$x, &$y ) {
1455 $z = $x;
1456 $x = $y;
1457 $y = $z;
1458 }
1459
1460 function wfGetCachedNotice( $name ) {
1461 global $wgOut, $parserMemc;
1462 $fname = 'wfGetCachedNotice';
1463 wfProfileIn( $fname );
1464
1465 $needParse = false;
1466
1467 if( $name === 'default' ) {
1468 // special case
1469 global $wgSiteNotice;
1470 $notice = $wgSiteNotice;
1471 if( empty( $notice ) ) {
1472 wfProfileOut( $fname );
1473 return false;
1474 }
1475 } else {
1476 $notice = wfMsgForContentNoTrans( $name );
1477 if( wfEmptyMsg( $name, $notice ) || $notice == '-' ) {
1478 wfProfileOut( $fname );
1479 return( false );
1480 }
1481 }
1482
1483 $cachedNotice = $parserMemc->get( wfMemcKey( $name ) );
1484 if( is_array( $cachedNotice ) ) {
1485 if( md5( $notice ) == $cachedNotice['hash'] ) {
1486 $notice = $cachedNotice['html'];
1487 } else {
1488 $needParse = true;
1489 }
1490 } else {
1491 $needParse = true;
1492 }
1493
1494 if( $needParse ) {
1495 if( is_object( $wgOut ) ) {
1496 $parsed = $wgOut->parse( $notice );
1497 $parserMemc->set( wfMemcKey( $name ), array( 'html' => $parsed, 'hash' => md5( $notice ) ), 600 );
1498 $notice = $parsed;
1499 } else {
1500 wfDebug( 'wfGetCachedNotice called for ' . $name . ' with no $wgOut available' );
1501 $notice = '';
1502 }
1503 }
1504
1505 wfProfileOut( $fname );
1506 return $notice;
1507 }
1508
1509 function wfGetNamespaceNotice() {
1510 global $wgTitle;
1511
1512 # Paranoia
1513 if ( !isset( $wgTitle ) || !is_object( $wgTitle ) )
1514 return "";
1515
1516 $fname = 'wfGetNamespaceNotice';
1517 wfProfileIn( $fname );
1518
1519 $key = "namespacenotice-" . $wgTitle->getNsText();
1520 $namespaceNotice = wfGetCachedNotice( $key );
1521 if ( $namespaceNotice && substr ( $namespaceNotice , 0 ,7 ) != "<p>&lt;" ) {
1522 $namespaceNotice = '<div id="namespacebanner">' . $namespaceNotice . "</div>";
1523 } else {
1524 $namespaceNotice = "";
1525 }
1526
1527 wfProfileOut( $fname );
1528 return $namespaceNotice;
1529 }
1530
1531 function wfGetSiteNotice() {
1532 global $wgUser, $wgSiteNotice;
1533 $fname = 'wfGetSiteNotice';
1534 wfProfileIn( $fname );
1535 $siteNotice = '';
1536
1537 if( wfRunHooks( 'SiteNoticeBefore', array( &$siteNotice ) ) ) {
1538 if( is_object( $wgUser ) && $wgUser->isLoggedIn() ) {
1539 $siteNotice = wfGetCachedNotice( 'sitenotice' );
1540 } else {
1541 $anonNotice = wfGetCachedNotice( 'anonnotice' );
1542 if( !$anonNotice ) {
1543 $siteNotice = wfGetCachedNotice( 'sitenotice' );
1544 } else {
1545 $siteNotice = $anonNotice;
1546 }
1547 }
1548 if( !$siteNotice ) {
1549 $siteNotice = wfGetCachedNotice( 'default' );
1550 }
1551 }
1552
1553 wfRunHooks( 'SiteNoticeAfter', array( &$siteNotice ) );
1554 wfProfileOut( $fname );
1555 return $siteNotice;
1556 }
1557
1558 /**
1559 * BC wrapper for MimeMagic::singleton()
1560 * @deprecated
1561 */
1562 function &wfGetMimeMagic() {
1563 return MimeMagic::singleton();
1564 }
1565
1566 /**
1567 * Tries to get the system directory for temporary files.
1568 * The TMPDIR, TMP, and TEMP environment variables are checked in sequence,
1569 * and if none are set /tmp is returned as the generic Unix default.
1570 *
1571 * NOTE: When possible, use the tempfile() function to create temporary
1572 * files to avoid race conditions on file creation, etc.
1573 *
1574 * @return string
1575 */
1576 function wfTempDir() {
1577 foreach( array( 'TMPDIR', 'TMP', 'TEMP' ) as $var ) {
1578 $tmp = getenv( $var );
1579 if( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
1580 return $tmp;
1581 }
1582 }
1583 # Hope this is Unix of some kind!
1584 return '/tmp';
1585 }
1586
1587 /**
1588 * Make directory, and make all parent directories if they don't exist
1589 */
1590 function wfMkdirParents( $fullDir, $mode = 0777 ) {
1591 if ( strval( $fullDir ) === '' ) {
1592 return true;
1593 }
1594
1595 # Go back through the paths to find the first directory that exists
1596 $currentDir = $fullDir;
1597 $createList = array();
1598 while ( strval( $currentDir ) !== '' && !file_exists( $currentDir ) ) {
1599 # Strip trailing slashes
1600 $currentDir = rtrim( $currentDir, '/\\' );
1601
1602 # Add to create list
1603 $createList[] = $currentDir;
1604
1605 # Find next delimiter searching from the end
1606 $p = max( strrpos( $currentDir, '/' ), strrpos( $currentDir, '\\' ) );
1607 if ( $p === false ) {
1608 $currentDir = false;
1609 } else {
1610 $currentDir = substr( $currentDir, 0, $p );
1611 }
1612 }
1613
1614 if ( count( $createList ) == 0 ) {
1615 # Directory specified already exists
1616 return true;
1617 } elseif ( $currentDir === false ) {
1618 # Went all the way back to root and it apparently doesn't exist
1619 return false;
1620 }
1621
1622 # Now go forward creating directories
1623 $createList = array_reverse( $createList );
1624 foreach ( $createList as $dir ) {
1625 # use chmod to override the umask, as suggested by the PHP manual
1626 if ( !mkdir( $dir, $mode ) || !chmod( $dir, $mode ) ) {
1627 return false;
1628 }
1629 }
1630 return true;
1631 }
1632
1633 /**
1634 * Increment a statistics counter
1635 */
1636 function wfIncrStats( $key ) {
1637 global $wgMemc;
1638 $key = wfMemcKey( 'stats', $key );
1639 if ( is_null( $wgMemc->incr( $key ) ) ) {
1640 $wgMemc->add( $key, 1 );
1641 }
1642 }
1643
1644 /**
1645 * @param mixed $nr The number to format
1646 * @param int $acc The number of digits after the decimal point, default 2
1647 * @param bool $round Whether or not to round the value, default true
1648 * @return float
1649 */
1650 function wfPercent( $nr, $acc = 2, $round = true ) {
1651 $ret = sprintf( "%.${acc}f", $nr );
1652 return $round ? round( $ret, $acc ) . '%' : "$ret%";
1653 }
1654
1655 /**
1656 * Encrypt a username/password.
1657 *
1658 * @param string $userid ID of the user
1659 * @param string $password Password of the user
1660 * @return string Hashed password
1661 */
1662 function wfEncryptPassword( $userid, $password ) {
1663 global $wgPasswordSalt;
1664 $p = md5( $password);
1665
1666 if($wgPasswordSalt)
1667 return md5( "{$userid}-{$p}" );
1668 else
1669 return $p;
1670 }
1671
1672 /**
1673 * Appends to second array if $value differs from that in $default
1674 */
1675 function wfAppendToArrayIfNotDefault( $key, $value, $default, &$changed ) {
1676 if ( is_null( $changed ) ) {
1677 throw new MWException('GlobalFunctions::wfAppendToArrayIfNotDefault got null');
1678 }
1679 if ( $default[$key] !== $value ) {
1680 $changed[$key] = $value;
1681 }
1682 }
1683
1684 /**
1685 * Since wfMsg() and co suck, they don't return false if the message key they
1686 * looked up didn't exist but a XHTML string, this function checks for the
1687 * nonexistance of messages by looking at wfMsg() output
1688 *
1689 * @param $msg The message key looked up
1690 * @param $wfMsgOut The output of wfMsg*()
1691 * @return bool
1692 */
1693 function wfEmptyMsg( $msg, $wfMsgOut ) {
1694 return $wfMsgOut === "&lt;$msg&gt;";
1695 }
1696
1697 /**
1698 * Find out whether or not a mixed variable exists in a string
1699 *
1700 * @param mixed needle
1701 * @param string haystack
1702 * @return bool
1703 */
1704 function in_string( $needle, $str ) {
1705 return strpos( $str, $needle ) !== false;
1706 }
1707
1708 function wfSpecialList( $page, $details ) {
1709 global $wgContLang;
1710 $details = $details ? ' ' . $wgContLang->getDirMark() . "($details)" : "";
1711 return $page . $details;
1712 }
1713
1714 /**
1715 * Returns a regular expression of url protocols
1716 *
1717 * @return string
1718 */
1719 function wfUrlProtocols() {
1720 global $wgUrlProtocols;
1721
1722 // Support old-style $wgUrlProtocols strings, for backwards compatibility
1723 // with LocalSettings files from 1.5
1724 if ( is_array( $wgUrlProtocols ) ) {
1725 $protocols = array();
1726 foreach ($wgUrlProtocols as $protocol)
1727 $protocols[] = preg_quote( $protocol, '/' );
1728
1729 return implode( '|', $protocols );
1730 } else {
1731 return $wgUrlProtocols;
1732 }
1733 }
1734
1735 /**
1736 * Execute a shell command, with time and memory limits mirrored from the PHP
1737 * configuration if supported.
1738 * @param $cmd Command line, properly escaped for shell.
1739 * @param &$retval optional, will receive the program's exit code.
1740 * (non-zero is usually failure)
1741 * @return collected stdout as a string (trailing newlines stripped)
1742 */
1743 function wfShellExec( $cmd, &$retval=null ) {
1744 global $IP, $wgMaxShellMemory, $wgMaxShellFileSize;
1745
1746 if( ini_get( 'safe_mode' ) ) {
1747 wfDebug( "wfShellExec can't run in safe_mode, PHP's exec functions are too broken.\n" );
1748 $retval = 1;
1749 return "Unable to run external programs in safe mode.";
1750 }
1751
1752 if ( php_uname( 's' ) == 'Linux' ) {
1753 $time = ini_get( 'max_execution_time' );
1754 $mem = intval( $wgMaxShellMemory );
1755 $filesize = intval( $wgMaxShellFileSize );
1756
1757 if ( $time > 0 && $mem > 0 ) {
1758 $script = "$IP/bin/ulimit-tvf.sh";
1759 if ( is_executable( $script ) ) {
1760 $cmd = escapeshellarg( $script ) . " $time $mem $filesize $cmd";
1761 }
1762 }
1763 } elseif ( php_uname( 's' ) == 'Windows NT' ) {
1764 # This is a hack to work around PHP's flawed invocation of cmd.exe
1765 # http://news.php.net/php.internals/21796
1766 $cmd = '"' . $cmd . '"';
1767 }
1768 wfDebug( "wfShellExec: $cmd\n" );
1769
1770 $output = array();
1771 $retval = 1; // error by default?
1772 exec( $cmd, $output, $retval ); // returns the last line of output.
1773 return implode( "\n", $output );
1774
1775 }
1776
1777 /**
1778 * This function works like "use VERSION" in Perl, the program will die with a
1779 * backtrace if the current version of PHP is less than the version provided
1780 *
1781 * This is useful for extensions which due to their nature are not kept in sync
1782 * with releases, and might depend on other versions of PHP than the main code
1783 *
1784 * Note: PHP might die due to parsing errors in some cases before it ever
1785 * manages to call this function, such is life
1786 *
1787 * @see perldoc -f use
1788 *
1789 * @param mixed $version The version to check, can be a string, an integer, or
1790 * a float
1791 */
1792 function wfUsePHP( $req_ver ) {
1793 $php_ver = PHP_VERSION;
1794
1795 if ( version_compare( $php_ver, (string)$req_ver, '<' ) )
1796 throw new MWException( "PHP $req_ver required--this is only $php_ver" );
1797 }
1798
1799 /**
1800 * This function works like "use VERSION" in Perl except it checks the version
1801 * of MediaWiki, the program will die with a backtrace if the current version
1802 * of MediaWiki is less than the version provided.
1803 *
1804 * This is useful for extensions which due to their nature are not kept in sync
1805 * with releases
1806 *
1807 * @see perldoc -f use
1808 *
1809 * @param mixed $version The version to check, can be a string, an integer, or
1810 * a float
1811 */
1812 function wfUseMW( $req_ver ) {
1813 global $wgVersion;
1814
1815 if ( version_compare( $wgVersion, (string)$req_ver, '<' ) )
1816 throw new MWException( "MediaWiki $req_ver required--this is only $wgVersion" );
1817 }
1818
1819 /**
1820 * @deprecated use StringUtils::escapeRegexReplacement
1821 */
1822 function wfRegexReplacement( $string ) {
1823 return StringUtils::escapeRegexReplacement( $string );
1824 }
1825
1826 /**
1827 * Return the final portion of a pathname.
1828 * Reimplemented because PHP5's basename() is buggy with multibyte text.
1829 * http://bugs.php.net/bug.php?id=33898
1830 *
1831 * PHP's basename() only considers '\' a pathchar on Windows and Netware.
1832 * We'll consider it so always, as we don't want \s in our Unix paths either.
1833 *
1834 * @param string $path
1835 * @return string
1836 */
1837 function wfBaseName( $path ) {
1838 $matches = array();
1839 if( preg_match( '#([^/\\\\]*)[/\\\\]*$#', $path, $matches ) ) {
1840 return $matches[1];
1841 } else {
1842 return '';
1843 }
1844 }
1845
1846 /**
1847 * Generate a relative path name to the given file.
1848 * May explode on non-matching case-insensitive paths,
1849 * funky symlinks, etc.
1850 *
1851 * @param string $path Absolute destination path including target filename
1852 * @param string $from Absolute source path, directory only
1853 * @return string
1854 */
1855 function wfRelativePath( $path, $from ) {
1856 $pieces = explode( DIRECTORY_SEPARATOR, dirname( $path ) );
1857 $against = explode( DIRECTORY_SEPARATOR, $from );
1858
1859 // Trim off common prefix
1860 while( count( $pieces ) && count( $against )
1861 && $pieces[0] == $against[0] ) {
1862 array_shift( $pieces );
1863 array_shift( $against );
1864 }
1865
1866 // relative dots to bump us to the parent
1867 while( count( $against ) ) {
1868 array_unshift( $pieces, '..' );
1869 array_shift( $against );
1870 }
1871
1872 array_push( $pieces, wfBaseName( $path ) );
1873
1874 return implode( DIRECTORY_SEPARATOR, $pieces );
1875 }
1876
1877 /**
1878 * Make a URL index, appropriate for the el_index field of externallinks.
1879 */
1880 function wfMakeUrlIndex( $url ) {
1881 wfSuppressWarnings();
1882 $bits = parse_url( $url );
1883 wfRestoreWarnings();
1884 if ( !$bits || $bits['scheme'] !== 'http' ) {
1885 return false;
1886 }
1887 // Reverse the labels in the hostname, convert to lower case
1888 $reversedHost = strtolower( implode( '.', array_reverse( explode( '.', $bits['host'] ) ) ) );
1889 // Add an extra dot to the end
1890 if ( substr( $reversedHost, -1, 1 ) !== '.' ) {
1891 $reversedHost .= '.';
1892 }
1893 // Reconstruct the pseudo-URL
1894 $index = "http://$reversedHost";
1895 // Leave out user and password. Add the port, path, query and fragment
1896 if ( isset( $bits['port'] ) ) $index .= ':' . $bits['port'];
1897 if ( isset( $bits['path'] ) ) {
1898 $index .= $bits['path'];
1899 } else {
1900 $index .= '/';
1901 }
1902 if ( isset( $bits['query'] ) ) $index .= '?' . $bits['query'];
1903 if ( isset( $bits['fragment'] ) ) $index .= '#' . $bits['fragment'];
1904 return $index;
1905 }
1906
1907 /**
1908 * Do any deferred updates and clear the list
1909 * TODO: This could be in Wiki.php if that class made any sense at all
1910 */
1911 function wfDoUpdates()
1912 {
1913 global $wgPostCommitUpdateList, $wgDeferredUpdateList;
1914 foreach ( $wgDeferredUpdateList as $update ) {
1915 $update->doUpdate();
1916 }
1917 foreach ( $wgPostCommitUpdateList as $update ) {
1918 $update->doUpdate();
1919 }
1920 $wgDeferredUpdateList = array();
1921 $wgPostCommitUpdateList = array();
1922 }
1923
1924 /**
1925 * @deprecated use StringUtils::explodeMarkup
1926 */
1927 function wfExplodeMarkup( $separator, $text ) {
1928 return StringUtils::explodeMarkup( $separator, $text );
1929 }
1930
1931 /**
1932 * Convert an arbitrarily-long digit string from one numeric base
1933 * to another, optionally zero-padding to a minimum column width.
1934 *
1935 * Supports base 2 through 36; digit values 10-36 are represented
1936 * as lowercase letters a-z. Input is case-insensitive.
1937 *
1938 * @param $input string of digits
1939 * @param $sourceBase int 2-36
1940 * @param $destBase int 2-36
1941 * @param $pad int 1 or greater
1942 * @return string or false on invalid input
1943 */
1944 function wfBaseConvert( $input, $sourceBase, $destBase, $pad=1 ) {
1945 if( $sourceBase < 2 ||
1946 $sourceBase > 36 ||
1947 $destBase < 2 ||
1948 $destBase > 36 ||
1949 $pad < 1 ||
1950 $sourceBase != intval( $sourceBase ) ||
1951 $destBase != intval( $destBase ) ||
1952 $pad != intval( $pad ) ||
1953 !is_string( $input ) ||
1954 $input == '' ) {
1955 return false;
1956 }
1957
1958 $digitChars = '0123456789abcdefghijklmnopqrstuvwxyz';
1959 $inDigits = array();
1960 $outChars = '';
1961
1962 // Decode and validate input string
1963 $input = strtolower( $input );
1964 for( $i = 0; $i < strlen( $input ); $i++ ) {
1965 $n = strpos( $digitChars, $input{$i} );
1966 if( $n === false || $n > $sourceBase ) {
1967 return false;
1968 }
1969 $inDigits[] = $n;
1970 }
1971
1972 // Iterate over the input, modulo-ing out an output digit
1973 // at a time until input is gone.
1974 while( count( $inDigits ) ) {
1975 $work = 0;
1976 $workDigits = array();
1977
1978 // Long division...
1979 foreach( $inDigits as $digit ) {
1980 $work *= $sourceBase;
1981 $work += $digit;
1982
1983 if( $work < $destBase ) {
1984 // Gonna need to pull another digit.
1985 if( count( $workDigits ) ) {
1986 // Avoid zero-padding; this lets us find
1987 // the end of the input very easily when
1988 // length drops to zero.
1989 $workDigits[] = 0;
1990 }
1991 } else {
1992 // Finally! Actual division!
1993 $workDigits[] = intval( $work / $destBase );
1994
1995 // Isn't it annoying that most programming languages
1996 // don't have a single divide-and-remainder operator,
1997 // even though the CPU implements it that way?
1998 $work = $work % $destBase;
1999 }
2000 }
2001
2002 // All that division leaves us with a remainder,
2003 // which is conveniently our next output digit.
2004 $outChars .= $digitChars[$work];
2005
2006 // And we continue!
2007 $inDigits = $workDigits;
2008 }
2009
2010 while( strlen( $outChars ) < $pad ) {
2011 $outChars .= '0';
2012 }
2013
2014 return strrev( $outChars );
2015 }
2016
2017 /**
2018 * Create an object with a given name and an array of construct parameters
2019 * @param string $name
2020 * @param array $p parameters
2021 */
2022 function wfCreateObject( $name, $p ){
2023 $p = array_values( $p );
2024 switch ( count( $p ) ) {
2025 case 0:
2026 return new $name;
2027 case 1:
2028 return new $name( $p[0] );
2029 case 2:
2030 return new $name( $p[0], $p[1] );
2031 case 3:
2032 return new $name( $p[0], $p[1], $p[2] );
2033 case 4:
2034 return new $name( $p[0], $p[1], $p[2], $p[3] );
2035 case 5:
2036 return new $name( $p[0], $p[1], $p[2], $p[3], $p[4] );
2037 case 6:
2038 return new $name( $p[0], $p[1], $p[2], $p[3], $p[4], $p[5] );
2039 default:
2040 throw new MWException( "Too many arguments to construtor in wfCreateObject" );
2041 }
2042 }
2043
2044 /**
2045 * Aliases for modularized functions
2046 */
2047 function wfGetHTTP( $url, $timeout = 'default' ) {
2048 return Http::get( $url, $timeout );
2049 }
2050 function wfIsLocalURL( $url ) {
2051 return Http::isLocalURL( $url );
2052 }
2053
2054 /**
2055 * Initialise php session
2056 */
2057 function wfSetupSession() {
2058 global $wgSessionsInMemcached, $wgCookiePath, $wgCookieDomain;
2059 if( $wgSessionsInMemcached ) {
2060 require_once( 'MemcachedSessions.php' );
2061 } elseif( 'files' != ini_get( 'session.save_handler' ) ) {
2062 # If it's left on 'user' or another setting from another
2063 # application, it will end up failing. Try to recover.
2064 ini_set ( 'session.save_handler', 'files' );
2065 }
2066 session_set_cookie_params( 0, $wgCookiePath, $wgCookieDomain );
2067 session_cache_limiter( 'private, must-revalidate' );
2068 @session_start();
2069 }
2070
2071 /**
2072 * Get an object from the precompiled serialized directory
2073 *
2074 * @return mixed The variable on success, false on failure
2075 */
2076 function wfGetPrecompiledData( $name ) {
2077 global $IP;
2078
2079 $file = "$IP/serialized/$name";
2080 if ( file_exists( $file ) ) {
2081 $blob = file_get_contents( $file );
2082 if ( $blob ) {
2083 return unserialize( $blob );
2084 }
2085 }
2086 return false;
2087 }
2088
2089 function wfGetCaller( $level = 2 ) {
2090 $backtrace = wfDebugBacktrace();
2091 if ( isset( $backtrace[$level] ) ) {
2092 if ( isset( $backtrace[$level]['class'] ) ) {
2093 $caller = $backtrace[$level]['class'] . '::' . $backtrace[$level]['function'];
2094 } else {
2095 $caller = $backtrace[$level]['function'];
2096 }
2097 } else {
2098 $caller = 'unknown';
2099 }
2100 return $caller;
2101 }
2102
2103 /** Return a string consisting all callers in stack, somewhat useful sometimes for profiling specific points */
2104 function wfGetAllCallers() {
2105 return implode('/', array_map(
2106 create_function('$frame','
2107 return isset( $frame["class"] )?
2108 $frame["class"]."::".$frame["function"]:
2109 $frame["function"];
2110 '),
2111 array_reverse(wfDebugBacktrace())));
2112 }
2113
2114 /**
2115 * Get a cache key
2116 */
2117 function wfMemcKey( /*... */ ) {
2118 global $wgDBprefix, $wgDBname;
2119 $args = func_get_args();
2120 if ( $wgDBprefix ) {
2121 $key = "$wgDBname-$wgDBprefix:" . implode( ':', $args );
2122 } else {
2123 $key = $wgDBname . ':' . implode( ':', $args );
2124 }
2125 return $key;
2126 }
2127
2128 /**
2129 * Get a cache key for a foreign DB
2130 */
2131 function wfForeignMemcKey( $db, $prefix /*, ... */ ) {
2132 $args = array_slice( func_get_args(), 2 );
2133 if ( $prefix ) {
2134 $key = "$db-$prefix:" . implode( ':', $args );
2135 } else {
2136 $key = $db . ':' . implode( ':', $args );
2137 }
2138 return $key;
2139 }
2140
2141 /**
2142 * Get an ASCII string identifying this wiki
2143 * This is used as a prefix in memcached keys
2144 */
2145 function wfWikiID() {
2146 global $wgDBprefix, $wgDBname;
2147 if ( $wgDBprefix ) {
2148 return "$wgDBname-$wgDBprefix";
2149 } else {
2150 return $wgDBname;
2151 }
2152 }
2153
2154 /*
2155 * Get a Database object
2156 * @param integer $db Index of the connection to get. May be DB_MASTER for the
2157 * master (for write queries), DB_SLAVE for potentially lagged
2158 * read queries, or an integer >= 0 for a particular server.
2159 *
2160 * @param mixed $groups Query groups. An array of group names that this query
2161 * belongs to. May contain a single string if the query is only
2162 * in one group.
2163 */
2164 function &wfGetDB( $db = DB_LAST, $groups = array() ) {
2165 global $wgLoadBalancer;
2166 $ret = $wgLoadBalancer->getConnection( $db, true, $groups );
2167 return $ret;
2168 }
2169 ?>