161 const FORMAT_PLAIN =
'plain';
163 const FORMAT_BLOCK_PARSE =
'block-parse';
165 const FORMAT_PARSE =
'parse';
167 const FORMAT_TEXT =
'text';
169 const FORMAT_ESCAPED =
'escaped';
175 protected static $listTypeMap = [
176 'comma' =>
'commaList',
177 'semicolon' =>
'semicolonList',
178 'pipe' =>
'pipeList',
179 'text' =>
'listToText',
188 protected $interface =
true;
195 protected $language =
false;
206 protected $keysToTry;
211 protected $parameters = [];
217 protected $format =
'parse';
222 protected $useDatabase =
true;
232 protected $content =
null;
248 public function __construct( $key,
$params = [],
Language $language =
null ) {
251 throw new InvalidArgumentException(
252 '$params must be empty if $key is a MessageSpecifier'
256 $key = $key->getKey();
259 if ( !is_string( $key ) && !is_array( $key ) ) {
260 throw new InvalidArgumentException(
'$key must be a string or an array' );
263 $this->keysToTry = (
array)$key;
265 if ( empty( $this->keysToTry ) ) {
266 throw new InvalidArgumentException(
'$key must not be an empty list' );
269 $this->
key = reset( $this->keysToTry );
271 $this->parameters = array_values(
$params );
274 $this->
language = $language ?:
false;
284 'interface' => $this->interface,
287 'keysToTry' => $this->keysToTry,
288 'parameters' => $this->parameters,
289 'format' => $this->format,
290 'useDatabase' => $this->useDatabase,
302 $this->
interface = $data['interface'];
303 $this->
key = $data[
'key'];
304 $this->keysToTry = $data[
'keysToTry'];
305 $this->parameters = $data[
'parameters'];
306 $this->
format = $data[
'format'];
307 $this->useDatabase = $data[
'useDatabase'];
309 $this->
title = $data[
'title'];
318 public function isMultiKey() {
319 return count( $this->keysToTry ) > 1;
328 public function getKeysToTry() {
329 return $this->keysToTry;
343 public function getKey() {
355 return $this->parameters;
366 public function getFormat() {
368 return $this->format;
378 public function getLanguage() {
395 public static function newFromKey( $key ) {
398 return new self( $key,
$params );
414 public static function newFromSpecifier(
$value ) {
416 if ( is_array(
$value ) ) {
421 if (
$value instanceof Message ) {
424 $message =
new Message(
$value );
425 } elseif ( is_string(
$value ) ) {
428 throw new InvalidArgumentException( __METHOD__ .
': invalid argument type '
447 public static function newFallbackSequence( ) {
448 $keys = func_get_args();
449 if ( func_num_args() == 1 ) {
450 if ( is_array(
$keys[0] ) ) {
458 return new self(
$keys );
471 public function getTitle() {
476 !$this->
language->equals( $wgContLang )
477 && in_array( $this->
key, (
array)$wgForceUIMsgAsContentMsg )
480 $title .=
'/' .
$code;
496 public function params( ) {
497 $args = func_get_args();
504 if (
$args[0] === [] ) {
508 if ( is_int( $key ) ) {
516 $this->parameters = array_merge( $this->parameters, array_values(
$args ) );
533 public function rawParams( ) {
539 $this->parameters[] = self::rawParam( $param );
555 public function numParams( ) {
561 $this->parameters[] = self::numParam( $param );
577 public function durationParams( ) {
583 $this->parameters[] = self::durationParam( $param );
599 public function expiryParams( ) {
605 $this->parameters[] = self::expiryParam( $param );
621 public function timeperiodParams( ) {
627 $this->parameters[] = self::timeperiodParam( $param );
643 public function sizeParams( ) {
649 $this->parameters[] = self::sizeParam( $param );
665 public function bitrateParams( ) {
671 $this->parameters[] = self::bitrateParam( $param );
689 public function plaintextParams( ) {
695 $this->parameters[] = self::plaintextParam( $param );
710 $this->inLanguage(
$context->getLanguage() );
712 $this->
interface =
true;
728 public function inLanguage(
$lang ) {
731 } elseif ( is_string(
$lang ) ) {
738 $type = gettype(
$lang );
740 .
"passed a String or Language object; $type given"
744 $this->
interface =
false;
757 public function inContentLanguage() {
759 if ( in_array( $this->
key, (
array)$wgForceUIMsgAsContentMsg ) ) {
778 public function setInterfaceMessageFlag( $interface ) {
779 $this->
interface = (bool)$interface;
792 public function useDatabase( $useDatabase ) {
793 $this->useDatabase = (bool)$useDatabase;
822 return $this->content;
835 public function toString( $format =
null ) {
836 if ( $format ===
null ) {
837 $ex =
new LogicException( __METHOD__ .
' using implicit format: ' . $this->
format );
839 $ex->getMessage(), [
'exception' => $ex,
'format' => $this->format,
'key' => $this->key ] );
840 $format = $this->format;
842 $string = $this->fetchMessage();
844 if ( $string ===
false ) {
852 return '⧼' . htmlspecialchars( $this->
key ) .
'⧽';
855 # Replace $* with a list of parameters for &uselang=qqx.
856 if ( strpos( $string,
'$*' ) !==
false ) {
858 if ( $this->parameters !== [] ) {
859 $paramlist =
': $' . implode(
', $', range( 1,
count( $this->parameters ) ) );
861 $string = str_replace(
'$*', $paramlist, $string );
864 # Replace parameters before text parsing
865 $string = $this->replaceParameters( $string,
'before', $format );
867 # Maybe transform using the full parser
868 if ( $format === self::FORMAT_PARSE ) {
869 $string = $this->parseText( $string );
870 $string = Parser::stripOuterParagraph( $string );
871 } elseif ( $format === self::FORMAT_BLOCK_PARSE ) {
872 $string = $this->parseText( $string );
873 } elseif ( $format === self::FORMAT_TEXT ) {
874 $string = $this->transformText( $string );
875 } elseif ( $format === self::FORMAT_ESCAPED ) {
876 $string = $this->transformText( $string );
877 $string = htmlspecialchars( $string, ENT_QUOTES,
'UTF-8',
false );
880 # Raw parameter replacement
881 $string = $this->replaceParameters( $string,
'after', $format );
895 public function __toString() {
900 return $this->toString( self::FORMAT_PARSE );
901 }
catch ( Exception $ex ) {
903 trigger_error(
"Exception caught in " . __METHOD__ .
" (message " . $this->
key .
"): "
904 . $ex, E_USER_WARNING );
905 }
catch ( Exception $ex ) {
909 return '⧼' . htmlspecialchars( $this->
key ) .
'⧽';
920 public function parse() {
921 $this->
format = self::FORMAT_PARSE;
922 return $this->toString( self::FORMAT_PARSE );
932 public function text() {
933 $this->
format = self::FORMAT_TEXT;
934 return $this->toString( self::FORMAT_TEXT );
944 public function plain() {
945 $this->
format = self::FORMAT_PLAIN;
946 return $this->toString( self::FORMAT_PLAIN );
956 public function parseAsBlock() {
957 $this->
format = self::FORMAT_BLOCK_PARSE;
958 return $this->toString( self::FORMAT_BLOCK_PARSE );
969 public function escaped() {
970 $this->
format = self::FORMAT_ESCAPED;
971 return $this->toString( self::FORMAT_ESCAPED );
981 public function exists() {
982 return $this->fetchMessage() !==
false;
993 public function isBlank() {
994 $message = $this->fetchMessage();
995 return $message ===
false || $message ===
'';
1005 public function isDisabled() {
1006 $message = $this->fetchMessage();
1007 return $message ===
false || $message ===
'' || $message ===
'-';
1017 public static function rawParam( $raw ) {
1018 return [
'raw' => $raw ];
1028 public static function numParam( $num ) {
1029 return [
'num' => $num ];
1039 public static function durationParam( $duration ) {
1040 return [
'duration' => $duration ];
1050 public static function expiryParam( $expiry ) {
1051 return [
'expiry' => $expiry ];
1061 public static function timeperiodParam( $period ) {
1062 return [
'period' => $period ];
1072 public static function sizeParam( $size ) {
1073 return [
'size' => $size ];
1083 public static function bitrateParam( $bitrate ) {
1084 return [
'bitrate' => $bitrate ];
1094 public static function plaintextParam( $plaintext ) {
1095 return [
'plaintext' => $plaintext ];
1105 public static function listParam(
array $list,
$type =
'text' ) {
1106 if ( !isset( self::$listTypeMap[
$type] ) ) {
1107 throw new InvalidArgumentException(
1108 "Invalid type '$type'. Known types are: " . join(
', ', array_keys( self::$listTypeMap ) )
1111 return [
'list' => $list,
'type' =>
$type ];
1125 protected function replaceParameters( $message,
$type =
'before', $format ) {
1130 $marker = $format === self::FORMAT_ESCAPED ?
'$' :
'$\'"';
1131 $replacementKeys = [];
1132 foreach ( $this->parameters
as $n => $param ) {
1133 list( $paramType,
$value ) = $this->extractParam( $param, $format );
1134 if (
$type ===
'before' ) {
1135 if ( $paramType ===
'before' ) {
1136 $replacementKeys[
'$' . ( $n + 1 )] =
$value;
1143 $replacementKeys[
'$' . ( $n + 1 )] = $marker . ( $n + 1 );
1146 if ( $paramType ===
'after' ) {
1147 $replacementKeys[$marker . ( $n + 1 )] =
$value;
1151 $message = strtr( $message, $replacementKeys );
1165 protected function extractParam( $param, $format ) {
1166 if ( is_array( $param ) ) {
1167 if ( isset( $param[
'raw'] ) ) {
1168 return [
'after', $param[
'raw'] ];
1169 } elseif ( isset( $param[
'num'] ) ) {
1172 return [
'before', $this->getLanguage()->formatNum( $param[
'num'] ) ];
1173 } elseif ( isset( $param[
'duration'] ) ) {
1174 return [
'before', $this->getLanguage()->formatDuration( $param[
'duration'] ) ];
1175 } elseif ( isset( $param[
'expiry'] ) ) {
1176 return [
'before', $this->getLanguage()->formatExpiry( $param[
'expiry'] ) ];
1177 } elseif ( isset( $param[
'period'] ) ) {
1178 return [
'before', $this->getLanguage()->formatTimePeriod( $param[
'period'] ) ];
1179 } elseif ( isset( $param[
'size'] ) ) {
1180 return [
'before', $this->getLanguage()->formatSize( $param[
'size'] ) ];
1181 } elseif ( isset( $param[
'bitrate'] ) ) {
1182 return [
'before', $this->getLanguage()->formatBitrate( $param[
'bitrate'] ) ];
1183 } elseif ( isset( $param[
'plaintext'] ) ) {
1184 return [
'after', $this->formatPlaintext( $param[
'plaintext'], $format ) ];
1185 } elseif ( isset( $param[
'list'] ) ) {
1186 return $this->formatListParam( $param[
'list'], $param[
'type'], $format );
1188 $warning =
'Invalid parameter for message "' . $this->
getKey() .
'": ' .
1189 htmlspecialchars(
serialize( $param ) );
1190 trigger_error( $warning, E_USER_WARNING );
1192 wfDebugLog(
'Bug58676', $warning .
"\n" .
$e->getTraceAsString() );
1194 return [
'before',
'[INVALID]' ];
1196 } elseif ( $param instanceof Message ) {
1198 $msg = clone $param;
1199 if ( $msg->language !== $this->language || $msg->useDatabase !== $this->useDatabase ) {
1201 $msg->message =
null;
1203 $msg->interface = $this->interface;
1204 $msg->language = $this->language;
1205 $msg->useDatabase = $this->useDatabase;
1209 if ( $format ===
'block-parse' ) {
1212 $msg->format = $format;
1217 return [
'after', $msg->toString( $format ) ];
1219 return [
'before', $param ];
1232 protected function parseText( $string ) {
1238 $this->getLanguage()
1253 protected function transformText( $string ) {
1257 $this->getLanguage(),
1270 protected function fetchMessage() {
1271 if ( $this->
message ===
null ) {
1274 foreach ( $this->keysToTry
as $key ) {
1275 $message =
$cache->get( $key, $this->useDatabase, $this->getLanguage() );
1276 if ( $message !==
false && $message !==
'' ) {
1286 return $this->message;
1301 protected function formatPlaintext( $plaintext, $format ) {
1302 switch ( $format ) {
1303 case self::FORMAT_TEXT:
1304 case self::FORMAT_PLAIN:
1307 case self::FORMAT_PARSE:
1308 case self::FORMAT_BLOCK_PARSE:
1309 case self::FORMAT_ESCAPED:
1311 return htmlspecialchars( $plaintext, ENT_QUOTES );
1324 protected function formatListParam(
array $params, $listType, $format ) {
1325 if ( !isset( self::$listTypeMap[$listType] ) ) {
1326 $warning =
'Invalid list type for message "' . $this->
getKey() .
'": '
1327 . htmlspecialchars( $listType )
1329 trigger_error( $warning, E_USER_WARNING );
1331 wfDebugLog(
'Bug58676', $warning .
"\n" .
$e->getTraceAsString() );
1332 return [
'before',
'[INVALID]' ];
1334 $func = self::$listTypeMap[$listType];
1338 return [
'before', $this->getLanguage()->$func( [] ) ];
1347 $types[
$type] =
true;
1349 $vars[] =
'$' . ( $n + 1 );
1354 if (
count( $types ) === 1 ) {
1355 return [
key( $types ), $this->getLanguage()->$func( $list ) ];
1361 $vars = $this->getLanguage()->$func(
$vars );