3 * This class provides methods for fetching interface messages and
4 * processing them into variety of formats that are needed in MediaWiki.
6 * It is intented to replace the old wfMsg* functions that over time grew
7 * unusable. \see https://www.mediawiki.org/wiki/New_messages_API for
8 * equivalence between old and new functions.
10 * Below, you will find several examples of wfMessage() usage.
13 * Fetching a message text for interface message
15 * $button = Xml::button( wfMessage( 'submit' )->text() );
18 * Messages can have parameters:
21 * wfMessage( 'welcome-to' )->params( $wgSitename )->text();
24 * {{GRAMMAR}} and friends work correctly
26 * wfMessage( 'are-friends', $user, $friend );
27 * wfMessage( 'bad-message' )->rawParams( '<script>...</script>' )->escaped();
30 * Sometimes the message text ends up in the database, so content language is needed.
32 * wfMessage( 'file-log', $user, $filename )->inContentLanguage()->text()
36 * Checking whether a message exists:
38 * wfMessage( 'mysterious-message' )->exists()
41 * If you want to use a different language:
43 * wfMessage( 'email-header' )->inLanguage( $user->getOption( 'language' ) )->plain()
46 * \note You cannot parse the text except in the content or interface
49 * Comparison with old wfMsg* functions:
54 * wfMsgExt( 'key', array( 'parseinline' ), 'apple' );
55 * === wfMessage( 'key', 'apple' )->parse();
58 * Parseinline is used because it is more useful when pre-building html.
59 * In normal use it is better to use OutputPage::(add|wrap)WikiMsg.
61 * Places where html cannot be used. {{-transformation is done.
63 * wfMsgExt( 'key', array( 'parsemag' ), 'apple', 'pear' );
64 * === wfMessage( 'key', 'apple', 'pear' )->text();
67 * Shortcut for escaping the message too, similar to wfMsgHTML, but
68 * parameters are not replaced after escaping by default.
70 * $escaped = wfMessage( 'key' )->rawParams( 'apple' )->escaped();
74 * - test, can we have tests?
76 * \see https://www.mediawiki.org/wiki/WfMessage()
77 * \see https://www.mediawiki.org/wiki/New_messages_API
78 * \see https://www.mediawiki.org/wiki/Localisation
81 * @author Niklas Laxström
85 * In which language to get this message. True, which is the default,
86 * means the current interface language, false content language.
88 protected $interface = true;
91 * In which language to get this message. Overrides the $interface
96 protected $language = null;
104 * List of parameters which will be substituted into the message.
106 protected $parameters = array();
109 * Format for the message.
110 * Supported formats are:
112 * * escaped (transform+htmlspecialchars)
117 protected $format = 'parse';
120 * Whether database can be used.
122 protected $useDatabase = true;
125 * Title object to use as context
127 protected $title = null;
136 * @param $key: message key, or array of message keys to try and use the first non-empty message for
137 * @param $params Array message parameters
138 * @return Message: $this
140 public function __construct( $key, $params = array() ) {
143 $this->parameters
= array_values( $params );
144 $this->language
= $wgLang;
148 * Factory function that is just wrapper for the real constructor. It is
149 * intented to be used instead of the real constructor, because it allows
150 * chaining method calls, while new objects don't.
151 * @param $key String: message key
152 * @param Varargs: parameters as Strings
153 * @return Message: $this
155 public static function newFromKey( $key /*...*/ ) {
156 $params = func_get_args();
157 array_shift( $params );
158 return new self( $key, $params );
162 * Factory function accepting multiple message keys and returning a message instance
163 * for the first message which is non-empty. If all messages are empty then an
164 * instance of the first message key is returned.
165 * @param Varargs: message keys (or first arg as an array of all the message keys)
166 * @return Message: $this
168 public static function newFallbackSequence( /*...*/ ) {
169 $keys = func_get_args();
170 if ( func_num_args() == 1 ) {
171 if ( is_array($keys[0]) ) {
172 // Allow an array to be passed as the first argument instead
173 $keys = array_values($keys[0]);
175 // Optimize a single string to not need special fallback handling
179 return new self( $keys );
183 * Adds parameters to the parameter list of this message.
184 * @param Varargs: parameters as Strings, or a single argument that is an array of Strings
185 * @return Message: $this
187 public function params( /*...*/ ) {
188 $args = func_get_args();
189 if ( isset( $args[0] ) && is_array( $args[0] ) ) {
192 $args_values = array_values( $args );
193 $this->parameters
= array_merge( $this->parameters
, $args_values );
198 * Add parameters that are substituted after parsing or escaping.
199 * In other words the parsing process cannot access the contents
200 * of this type of parameter, and you need to make sure it is
201 * sanitized beforehand. The parser will see "$n", instead.
202 * @param Varargs: raw parameters as Strings (or single argument that is an array of raw parameters)
203 * @return Message: $this
205 public function rawParams( /*...*/ ) {
206 $params = func_get_args();
207 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
208 $params = $params[0];
210 foreach( $params as $param ) {
211 $this->parameters
[] = self
::rawParam( $param );
217 * Add parameters that are numeric and will be passed through
218 * Language::formatNum before substitution
219 * @param Varargs: numeric parameters (or single argument that is array of numeric parameters)
220 * @return Message: $this
222 public function numParams( /*...*/ ) {
223 $params = func_get_args();
224 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
225 $params = $params[0];
227 foreach( $params as $param ) {
228 $this->parameters
[] = self
::numParam( $param );
234 * Set the language and the title from a context object
236 * @param $context IContextSource
237 * @return Message: $this
239 public function setContext( IContextSource
$context ) {
240 $this->inLanguage( $context->getLanguage() );
241 $this->title( $context->getTitle() );
247 * Request the message in any language that is supported.
248 * As a side effect interface message status is unconditionally
250 * @param $lang Mixed: language code or Language object.
251 * @return Message: $this
253 public function inLanguage( $lang ) {
254 if ( $lang instanceof Language ||
$lang instanceof StubUserLang
) {
255 $this->language
= $lang;
256 } elseif ( is_string( $lang ) ) {
257 if( $this->language
->getCode() != $lang ) {
258 $this->language
= Language
::factory( $lang );
261 $type = gettype( $lang );
262 throw new MWException( __METHOD__
. " must be "
263 . "passed a String or Language object; $type given"
266 $this->interface = false;
271 * Request the message in the wiki's content language,
272 * unless it is disabled for this message.
273 * @see $wgForceUIMsgAsContentMsg
274 * @return Message: $this
276 public function inContentLanguage() {
277 global $wgForceUIMsgAsContentMsg;
278 if ( in_array( $this->key
, (array)$wgForceUIMsgAsContentMsg ) ) {
283 $this->interface = false;
284 $this->language
= $wgContLang;
289 * Enable or disable database use.
290 * @param $value Boolean
291 * @return Message: $this
293 public function useDatabase( $value ) {
294 $this->useDatabase
= (bool) $value;
299 * Set the Title object to use as context when transforming the message
301 * @param $title Title object
302 * @return Message: $this
304 public function title( $title ) {
305 $this->title
= $title;
310 * Returns the message parsed from wikitext to HTML.
311 * @return String: HTML
313 public function toString() {
314 $string = $this->getMessageText();
316 # Replace parameters before text parsing
317 $string = $this->replaceParameters( $string, 'before' );
319 # Maybe transform using the full parser
320 if( $this->format
=== 'parse' ) {
321 $string = $this->parseText( $string );
323 if( preg_match( '/^<p>(.*)\n?<\/p>\n?$/sU', $string, $m ) ) {
326 } elseif( $this->format
=== 'block-parse' ){
327 $string = $this->parseText( $string );
328 } elseif( $this->format
=== 'text' ){
329 $string = $this->transformText( $string );
330 } elseif( $this->format
=== 'escaped' ){
331 $string = $this->transformText( $string );
332 $string = htmlspecialchars( $string, ENT_QUOTES
, 'UTF-8', false );
335 # Raw parameter replacement
336 $string = $this->replaceParameters( $string, 'after' );
342 * Magic method implementation of the above (for PHP >= 5.2.0), so we can do, eg:
343 * $foo = Message::get($key);
344 * $string = "<abbr>$foo</abbr>";
347 public function __toString() {
348 return $this->toString();
352 * Fully parse the text from wikitext to HTML
353 * @return String parsed HTML
355 public function parse() {
356 $this->format
= 'parse';
357 return $this->toString();
361 * Returns the message text. {{-transformation is done.
362 * @return String: Unescaped message text.
364 public function text() {
365 $this->format
= 'text';
366 return $this->toString();
370 * Returns the message text as-is, only parameters are subsituted.
371 * @return String: Unescaped untransformed message text.
373 public function plain() {
374 $this->format
= 'plain';
375 return $this->toString();
379 * Returns the parsed message text which is always surrounded by a block element.
380 * @return String: HTML
382 public function parseAsBlock() {
383 $this->format
= 'block-parse';
384 return $this->toString();
388 * Returns the message text. {{-transformation is done and the result
389 * is escaped excluding any raw parameters.
390 * @return String: Escaped message text.
392 public function escaped() {
393 $this->format
= 'escaped';
394 return $this->toString();
398 * Check whether a message key has been defined currently.
399 * @return Bool: true if it is and false if not.
401 public function exists() {
402 return $this->fetchMessage() !== false;
406 * Check whether a message does not exist, or is an empty string
407 * @return Bool: true if is is and false if not
408 * @todo FIXME: Merge with isDisabled()?
410 public function isBlank() {
411 $message = $this->fetchMessage();
412 return $message === false ||
$message === '';
416 * Check whether a message does not exist, is an empty string, or is "-"
417 * @return Bool: true if is is and false if not
419 public function isDisabled() {
420 $message = $this->fetchMessage();
421 return $message === false ||
$message === '' ||
$message === '-';
428 public static function rawParam( $value ) {
429 return array( 'raw' => $value );
436 public static function numParam( $value ) {
437 return array( 'num' => $value );
441 * Substitutes any paramaters into the message text.
442 * @param $message String: the message text
443 * @param $type String: either before or after
446 protected function replaceParameters( $message, $type = 'before' ) {
447 $replacementKeys = array();
448 foreach( $this->parameters
as $n => $param ) {
449 list( $paramType, $value ) = $this->extractParam( $param );
450 if ( $type === $paramType ) {
451 $replacementKeys['$' . ($n +
1)] = $value;
454 $message = strtr( $message, $replacementKeys );
459 * Extracts the parameter type and preprocessed the value if needed.
460 * @param $param String|Array: Parameter as defined in this class.
461 * @return Tuple(type, value)
462 * @throws MWException
464 protected function extractParam( $param ) {
465 if ( is_array( $param ) && isset( $param['raw'] ) ) {
466 return array( 'after', $param['raw'] );
467 } elseif ( is_array( $param ) && isset( $param['num'] ) ) {
468 // Replace number params always in before step for now.
469 // No support for combined raw and num params
470 return array( 'before', $this->language
->formatNum( $param['num'] ) );
471 } elseif ( !is_array( $param ) ) {
472 return array( 'before', $param );
474 throw new MWException( "Invalid message parameter" );
479 * Wrapper for what ever method we use to parse wikitext.
480 * @param $string String: Wikitext message contents
481 * @return string Wikitext parsed into HTML
483 protected function parseText( $string ) {
484 return MessageCache
::singleton()->parse( $string, $this->title
, /*linestart*/true, $this->interface, $this->language
)->getText();
488 * Wrapper for what ever method we use to {{-transform wikitext.
489 * @param $string String: Wikitext message contents
490 * @return string Wikitext with {{-constructs replaced with their values.
492 protected function transformText( $string ) {
493 return MessageCache
::singleton()->transform( $string, $this->interface, $this->language
, $this->title
);
497 * Returns the textual value for the message.
498 * @return Message contents or placeholder
500 protected function getMessageText() {
501 $message = $this->fetchMessage();
502 if ( $message === false ) {
503 return '<' . htmlspecialchars( is_array($this->key
) ?
$this->key
[0] : $this->key
) . '>';
510 * Wrapper for what ever method we use to get message contents
514 protected function fetchMessage() {
515 if ( !isset( $this->message
) ) {
516 $cache = MessageCache
::singleton();
517 if ( is_array($this->key
) ) {
518 foreach ( $this->key
as $key ) {
519 $message = $cache->get( $key, $this->useDatabase
, $this->language
);
520 if ( $message !== false && $message !== '' ) {
524 $this->message
= $message;
526 $this->message
= $cache->get( $this->key
, $this->useDatabase
, $this->language
);
529 return $this->message
;