164 const FORMAT_PLAIN =
'plain';
166 const FORMAT_BLOCK_PARSE =
'block-parse';
168 const FORMAT_PARSE =
'parse';
170 const FORMAT_TEXT =
'text';
172 const FORMAT_ESCAPED =
'escaped';
178 protected static $listTypeMap = [
179 'comma' =>
'commaList',
180 'semicolon' =>
'semicolonList',
181 'pipe' =>
'pipeList',
182 'text' =>
'listToText',
191 protected $interface =
true;
198 protected $language =
false;
209 protected $keysToTry;
214 protected $parameters = [];
220 protected $format =
'parse';
225 protected $useDatabase =
true;
251 public function __construct( $key, $params = [],
Language $language =
null ) {
254 throw new InvalidArgumentException(
255 '$params must be empty if $key is a MessageSpecifier'
258 $params = $key->getParams();
259 $key = $key->getKey();
262 if ( !is_string( $key ) && !is_array( $key ) ) {
263 throw new InvalidArgumentException(
'$key must be a string or an array' );
266 $this->keysToTry = (array)$key;
268 if ( empty( $this->keysToTry ) ) {
269 throw new InvalidArgumentException(
'$key must not be an empty list' );
272 $this->key = reset( $this->keysToTry );
274 $this->parameters = array_values( $params );
277 $this->language = $language ?:
false;
287 'interface' => $this->interface,
288 'language' => $this->language ? $this->language->getCode() :
false,
290 'keysToTry' => $this->keysToTry,
291 'parameters' => $this->parameters,
292 'format' => $this->format,
293 'useDatabase' => $this->useDatabase,
294 'titlestr' => $this->title ? $this->title->getFullText() :
null,
305 if ( !is_array( $data ) ) {
306 throw new InvalidArgumentException( __METHOD__ .
': Invalid serialized data' );
309 $this->
interface = $data['interface'];
310 $this->key = $data[
'key'];
311 $this->keysToTry = $data[
'keysToTry'];
312 $this->parameters = $data[
'parameters'];
313 $this->format = $data[
'format'];
314 $this->useDatabase = $data[
'useDatabase'];
315 $this->language = $data[
'language'] ?
Language::factory( $data[
'language'] ) : false;
317 if ( isset( $data[
'titlestr'] ) ) {
319 } elseif ( isset( $data[
'title'] ) && $data[
'title'] instanceof
Title ) {
321 $this->title = $data[
'title'];
333 public function isMultiKey() {
334 return count( $this->keysToTry ) > 1;
343 public function getKeysToTry() {
344 return $this->keysToTry;
358 public function getKey() {
370 return $this->parameters;
381 public function getFormat() {
383 return $this->format;
393 public function getLanguage() {
410 public static function newFromKey( $key, ...$params ) {
411 return new self( $key, $params );
427 public static function newFromSpecifier( $value ) {
429 if ( is_array( $value ) ) {
431 $value = array_shift( $params );
434 if ( $value instanceof
Message ) {
435 $message = clone $value;
437 $message =
new Message( $value );
438 } elseif ( is_string( $value ) ) {
439 $message =
new Message( $value, $params );
441 throw new InvalidArgumentException( __METHOD__ .
': invalid argument type '
442 . gettype( $value ) );
460 public static function newFallbackSequence( ...
$keys ) {
461 if ( func_num_args() == 1 ) {
462 if ( is_array(
$keys[0] ) ) {
470 return new self(
$keys );
486 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
487 $lang = $this->getLanguage();
490 !
$lang->equals( $contLang )
510 public function params( ...
$args ) {
515 if ( count(
$args ) === 1 && isset(
$args[0] ) && is_array(
$args[0] ) ) {
516 if (
$args[0] === [] ) {
519 foreach (
$args[0] as $key => $value ) {
520 if ( is_int( $key ) ) {
528 $this->parameters = array_merge( $this->parameters, array_values(
$args ) );
545 public function rawParams( ...$params ) {
546 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
547 $params = $params[0];
549 foreach ( $params as $param ) {
550 $this->parameters[] = self::rawParam( $param );
566 public function numParams( ...$params ) {
567 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
568 $params = $params[0];
570 foreach ( $params as $param ) {
571 $this->parameters[] = self::numParam( $param );
587 public function durationParams( ...$params ) {
588 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
589 $params = $params[0];
591 foreach ( $params as $param ) {
592 $this->parameters[] = self::durationParam( $param );
608 public function expiryParams( ...$params ) {
609 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
610 $params = $params[0];
612 foreach ( $params as $param ) {
613 $this->parameters[] = self::expiryParam( $param );
629 public function timeperiodParams( ...$params ) {
630 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
631 $params = $params[0];
633 foreach ( $params as $param ) {
634 $this->parameters[] = self::timeperiodParam( $param );
650 public function sizeParams( ...$params ) {
651 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
652 $params = $params[0];
654 foreach ( $params as $param ) {
655 $this->parameters[] = self::sizeParam( $param );
671 public function bitrateParams( ...$params ) {
672 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
673 $params = $params[0];
675 foreach ( $params as $param ) {
676 $this->parameters[] = self::bitrateParam( $param );
694 public function plaintextParams( ...$params ) {
695 if ( isset( $params[0] ) && is_array( $params[0] ) ) {
696 $params = $params[0];
698 foreach ( $params as $param ) {
699 $this->parameters[] = self::plaintextParam( $param );
714 $this->inLanguage(
$context->getLanguage() );
715 $this->title(
$context->getTitle() );
716 $this->
interface =
true;
732 public function inLanguage(
$lang ) {
733 $previousLanguage = $this->language;
736 $this->language =
$lang;
737 } elseif ( is_string(
$lang ) ) {
742 $this->language =
false;
746 .
"passed a String or Language object; $type given"
750 if ( $this->language !== $previousLanguage ) {
752 $this->message =
null;
754 $this->
interface = false;
767 public function inContentLanguage() {
773 $this->inLanguage( MediaWikiServices::getInstance()->getContentLanguage() );
787 public function setInterfaceMessageFlag( $interface ) {
788 $this->
interface = (bool)$interface;
801 public function useDatabase( $useDatabase ) {
802 $this->useDatabase = (bool)$useDatabase;
803 $this->message =
null;
816 public function title(
$title ) {
826 public function content() {
827 if ( !$this->content ) {
845 public function toString( $format =
null ) {
846 if ( $format ===
null ) {
847 $ex =
new LogicException( __METHOD__ .
' using implicit format: ' . $this->format );
848 LoggerFactory::getInstance(
'message-format' )->warning(
849 $ex->getMessage(), [
'exception' => $ex,
'format' => $this->format,
'key' => $this->key ] );
850 $format = $this->format;
852 $string = $this->fetchMessage();
854 if ( $string ===
false ) {
862 return '⧼' . htmlspecialchars( $this->key ) .
'⧽';
865 # Replace $* with a list of parameters for &uselang=qqx.
866 if ( strpos( $string,
'$*' ) !==
false ) {
868 if ( $this->parameters !== [] ) {
869 $paramlist =
': $' . implode(
', $', range( 1, count( $this->parameters ) ) );
871 $string = str_replace(
'$*', $paramlist, $string );
874 # Replace parameters before text parsing
875 $string = $this->replaceParameters( $string,
'before', $format );
877 # Maybe transform using the full parser
878 if ( $format === self::FORMAT_PARSE ) {
879 $string = $this->parseText( $string );
880 $string = Parser::stripOuterParagraph( $string );
881 } elseif ( $format === self::FORMAT_BLOCK_PARSE ) {
882 $string = $this->parseText( $string );
883 } elseif ( $format === self::FORMAT_TEXT ) {
884 $string = $this->transformText( $string );
885 } elseif ( $format === self::FORMAT_ESCAPED ) {
886 $string = $this->transformText( $string );
887 $string = htmlspecialchars( $string, ENT_QUOTES,
'UTF-8',
false );
890 # Raw parameter replacement
891 $string = $this->replaceParameters( $string,
'after', $format );
905 public function __toString() {
910 return $this->toString( self::FORMAT_PARSE );
911 }
catch ( Exception $ex ) {
913 trigger_error(
"Exception caught in " . __METHOD__ .
" (message " . $this->key .
"): "
914 . $ex, E_USER_WARNING );
915 }
catch ( Exception $ex ) {
919 return '⧼' . htmlspecialchars( $this->key ) .
'⧽';
930 public function parse() {
931 $this->format = self::FORMAT_PARSE;
932 return $this->toString( self::FORMAT_PARSE );
942 public function text() {
943 $this->format = self::FORMAT_TEXT;
944 return $this->toString( self::FORMAT_TEXT );
954 public function plain() {
955 $this->format = self::FORMAT_PLAIN;
956 return $this->toString( self::FORMAT_PLAIN );
966 public function parseAsBlock() {
967 $this->format = self::FORMAT_BLOCK_PARSE;
968 return $this->toString( self::FORMAT_BLOCK_PARSE );
979 public function escaped() {
980 $this->format = self::FORMAT_ESCAPED;
981 return $this->toString( self::FORMAT_ESCAPED );
991 public function exists() {
992 return $this->fetchMessage() !==
false;
1003 public function isBlank() {
1004 $message = $this->fetchMessage();
1005 return $message ===
false || $message ===
'';
1015 public function isDisabled() {
1016 $message = $this->fetchMessage();
1017 return $message ===
false || $message ===
'' || $message ===
'-';
1027 public static function rawParam( $raw ) {
1028 return [
'raw' => $raw ];
1038 public static function numParam( $num ) {
1039 return [
'num' => $num ];
1049 public static function durationParam( $duration ) {
1050 return [
'duration' => $duration ];
1060 public static function expiryParam( $expiry ) {
1061 return [
'expiry' => $expiry ];
1071 public static function timeperiodParam( $period ) {
1072 return [
'period' => $period ];
1082 public static function sizeParam( $size ) {
1083 return [
'size' => $size ];
1093 public static function bitrateParam( $bitrate ) {
1094 return [
'bitrate' => $bitrate ];
1104 public static function plaintextParam( $plaintext ) {
1105 return [
'plaintext' => $plaintext ];
1115 public static function listParam( array $list,
$type =
'text' ) {
1116 if ( !isset( self::$listTypeMap[
$type] ) ) {
1117 throw new InvalidArgumentException(
1118 "Invalid type '$type'. Known types are: " . implode(
', ', array_keys( self::$listTypeMap ) )
1121 return [
'list' => $list,
'type' =>
$type ];
1135 protected function replaceParameters( $message,
$type, $format ) {
1140 $marker = $format === self::FORMAT_ESCAPED ?
'$' :
'$\'"';
1141 $replacementKeys = [];
1142 foreach ( $this->parameters as $n => $param ) {
1143 list( $paramType, $value ) = $this->extractParam( $param, $format );
1144 if (
$type ===
'before' ) {
1145 if ( $paramType ===
'before' ) {
1146 $replacementKeys[
'$' . ( $n + 1 )] = $value;
1153 $replacementKeys[
'$' . ( $n + 1 )] = $marker . ( $n + 1 );
1155 } elseif ( $paramType ===
'after' ) {
1156 $replacementKeys[$marker . ( $n + 1 )] = $value;
1159 return strtr( $message, $replacementKeys );
1172 protected function extractParam( $param, $format ) {
1173 if ( is_array( $param ) ) {
1174 if ( isset( $param[
'raw'] ) ) {
1175 return [
'after', $param[
'raw'] ];
1176 } elseif ( isset( $param[
'num'] ) ) {
1179 return [
'before', $this->getLanguage()->formatNum( $param[
'num'] ) ];
1180 } elseif ( isset( $param[
'duration'] ) ) {
1181 return [
'before', $this->getLanguage()->formatDuration( $param[
'duration'] ) ];
1182 } elseif ( isset( $param[
'expiry'] ) ) {
1183 return [
'before', $this->getLanguage()->formatExpiry( $param[
'expiry'] ) ];
1184 } elseif ( isset( $param[
'period'] ) ) {
1185 return [
'before', $this->getLanguage()->formatTimePeriod( $param[
'period'] ) ];
1186 } elseif ( isset( $param[
'size'] ) ) {
1187 return [
'before', $this->getLanguage()->formatSize( $param[
'size'] ) ];
1188 } elseif ( isset( $param[
'bitrate'] ) ) {
1189 return [
'before', $this->getLanguage()->formatBitrate( $param[
'bitrate'] ) ];
1190 } elseif ( isset( $param[
'plaintext'] ) ) {
1191 return [
'after', $this->formatPlaintext( $param[
'plaintext'], $format ) ];
1192 } elseif ( isset( $param[
'list'] ) ) {
1193 return $this->formatListParam( $param[
'list'], $param[
'type'], $format );
1195 if ( !is_scalar( $param ) ) {
1198 LoggerFactory::getInstance(
'Bug58676' )->warning(
1199 'Invalid parameter for message "{msgkey}": {param}',
1201 'exception' =>
new Exception,
1202 'msgkey' => $this->
getKey(),
1203 'param' => htmlspecialchars( $param ),
1207 return [
'before',
'[INVALID]' ];
1209 } elseif ( $param instanceof
Message ) {
1211 $msg = clone $param;
1212 if ( $msg->language !== $this->language || $msg->useDatabase !== $this->useDatabase ) {
1214 $msg->message =
null;
1216 $msg->interface = $this->interface;
1217 $msg->language = $this->language;
1218 $msg->useDatabase = $this->useDatabase;
1222 if ( $format ===
'block-parse' ) {
1225 $msg->format = $format;
1230 return [
'after', $msg->toString( $format ) ];
1232 return [
'before', $param ];
1245 protected function parseText( $string ) {
1251 $this->getLanguage()
1256 'enableSectionEditLinks' =>
false,
1275 protected function transformText( $string ) {
1279 $this->getLanguage(),
1292 protected function fetchMessage() {
1293 if ( $this->message ===
null ) {
1296 foreach ( $this->keysToTry as $key ) {
1297 $message =
$cache->get( $key, $this->useDatabase, $this->getLanguage() );
1298 if ( $message !==
false && $message !==
'' ) {
1306 $this->message = $message;
1308 return $this->message;
1323 protected function formatPlaintext( $plaintext, $format ) {
1324 switch ( $format ) {
1325 case self::FORMAT_TEXT:
1326 case self::FORMAT_PLAIN:
1329 case self::FORMAT_PARSE:
1330 case self::FORMAT_BLOCK_PARSE:
1331 case self::FORMAT_ESCAPED:
1333 return htmlspecialchars( $plaintext, ENT_QUOTES );
1345 protected function formatListParam( array $params, $listType, $format ) {
1346 if ( !isset( self::$listTypeMap[$listType] ) ) {
1347 $warning =
'Invalid list type for message "' . $this->
getKey() .
'": '
1348 . htmlspecialchars( $listType )
1349 .
' (params are ' . htmlspecialchars(
serialize( $params ) ) .
')';
1350 trigger_error( $warning, E_USER_WARNING );
1352 wfDebugLog(
'Bug58676', $warning .
"\n" . $e->getTraceAsString() );
1353 return [
'before',
'[INVALID]' ];
1355 $func = self::$listTypeMap[$listType];
1359 return [
'before', $this->getLanguage()->$func( [] ) ];
1366 foreach ( $params as $n => $p ) {
1367 list(
$type, $value ) = $this->extractParam( $p, $format );
1368 $types[
$type] =
true;
1370 $vars[] =
'$' . ( $n + 1 );
1375 if ( count( $types ) === 1 ) {
1376 return [ key( $types ), $this->getLanguage()->$func( $list ) ];
1382 $vars = $this->getLanguage()->$func( $vars );
1383 return $this->extractParam(
new RawMessage( $vars, $params ), $format );