Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
98.25% |
56 / 57 |
|
96.55% |
28 / 29 |
CRAP | |
0.00% |
0 / 1 |
| MessageValue | |
98.25% |
56 / 57 |
|
96.55% |
28 / 29 |
38 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
| new | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| newFromSpecifier | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
| getKey | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| getParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| params | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| textParamsOfType | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| listParamsOfType | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
| textParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| numParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| longDurationParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| shortDurationParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| expiryParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| dateTimeParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| dateParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| timeParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| userGroupParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| sizeParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| bitrateParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| rawParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| plaintextParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| commaListParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| semicolonListParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| pipeListParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| textListParams | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| dump | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
| isSameAs | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
| toJsonArray | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
| newFromJsonArray | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License as published by |
| 5 | * the Free Software Foundation; either version 2 of the License, or |
| 6 | * (at your option) any later version. |
| 7 | * |
| 8 | * This program is distributed in the hope that it will be useful, |
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | * GNU General Public License for more details. |
| 12 | * |
| 13 | * You should have received a copy of the GNU General Public License along |
| 14 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 15 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| 16 | * http://www.gnu.org/copyleft/gpl.html |
| 17 | * |
| 18 | * @file |
| 19 | */ |
| 20 | namespace Wikimedia\Message; |
| 21 | |
| 22 | use Wikimedia\Assert\Assert; |
| 23 | use Wikimedia\JsonCodec\JsonCodecable; |
| 24 | use Wikimedia\JsonCodec\JsonCodecableTrait; |
| 25 | |
| 26 | /** |
| 27 | * Value object representing a message for i18n. |
| 28 | * |
| 29 | * A MessageValue holds a key and an array of parameters. It can be converted |
| 30 | * to a string in a particular language using formatters obtained from an |
| 31 | * IMessageFormatterFactory. |
| 32 | * |
| 33 | * MessageValues are pure value objects and are newable and (de)serializable. |
| 34 | * |
| 35 | * @newable |
| 36 | */ |
| 37 | class MessageValue implements MessageSpecifier, JsonCodecable { |
| 38 | use JsonCodecableTrait; |
| 39 | |
| 40 | private string $key; |
| 41 | |
| 42 | /** @var list<MessageParam> */ |
| 43 | private array $params; |
| 44 | |
| 45 | /** |
| 46 | * @stable to call |
| 47 | * |
| 48 | * @param string $key |
| 49 | * @param (MessageParam|MessageSpecifier|string|int|float)[] $params Values that are not instances |
| 50 | * of MessageParam are wrapped using ParamType::TEXT. |
| 51 | */ |
| 52 | public function __construct( string $key, array $params = [] ) { |
| 53 | $this->key = $key; |
| 54 | $this->params = []; |
| 55 | $this->params( ...$params ); |
| 56 | // @phan-suppress-next-line PhanRedundantCondition phan doesn't see side-effects on $this->params |
| 57 | Assert::invariant( array_is_list( $this->params ), "should be list" ); |
| 58 | } |
| 59 | |
| 60 | /** |
| 61 | * Static constructor for easier chaining of `->params()` methods |
| 62 | * @param string $key |
| 63 | * @param (MessageParam|MessageSpecifier|string|int|float)[] $params |
| 64 | * @return MessageValue |
| 65 | */ |
| 66 | public static function new( string $key, array $params = [] ): MessageValue { |
| 67 | return new MessageValue( $key, $params ); |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Convert from any MessageSpecifier to a MessageValue. |
| 72 | * |
| 73 | * When the given object is an instance of MessageValue, the same object is returned. |
| 74 | * |
| 75 | * @since MediaWiki 1.43 |
| 76 | * @param MessageSpecifier $spec |
| 77 | * @return MessageValue |
| 78 | */ |
| 79 | public static function newFromSpecifier( MessageSpecifier $spec ): MessageValue { |
| 80 | if ( $spec instanceof MessageValue ) { |
| 81 | return $spec; |
| 82 | } |
| 83 | return new MessageValue( $spec->getKey(), $spec->getParams() ); |
| 84 | } |
| 85 | |
| 86 | /** |
| 87 | * Get the message key |
| 88 | * |
| 89 | * @return string |
| 90 | */ |
| 91 | public function getKey(): string { |
| 92 | return $this->key; |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * Get the parameter array |
| 97 | * |
| 98 | * @return MessageParam[] |
| 99 | */ |
| 100 | public function getParams(): array { |
| 101 | return $this->params; |
| 102 | } |
| 103 | |
| 104 | /** |
| 105 | * Chainable mutator which adds text parameters and MessageParam parameters |
| 106 | * |
| 107 | * @param MessageParam|MessageSpecifier|string|int|float ...$values |
| 108 | * @return $this |
| 109 | */ |
| 110 | public function params( ...$values ): MessageValue { |
| 111 | foreach ( $values as $value ) { |
| 112 | if ( $value instanceof MessageParam ) { |
| 113 | $this->params[] = $value; |
| 114 | } else { |
| 115 | $this->params[] = new ScalarParam( ParamType::TEXT, $value ); |
| 116 | } |
| 117 | } |
| 118 | return $this; |
| 119 | } |
| 120 | |
| 121 | /** |
| 122 | * Chainable mutator which adds text parameters with a common type |
| 123 | * |
| 124 | * @param string $type One of the ParamType constants |
| 125 | * @param MessageSpecifier|string|int|float ...$values Scalar values |
| 126 | * @return $this |
| 127 | */ |
| 128 | public function textParamsOfType( string $type, ...$values ): MessageValue { |
| 129 | foreach ( $values as $value ) { |
| 130 | $this->params[] = new ScalarParam( $type, $value ); |
| 131 | } |
| 132 | return $this; |
| 133 | } |
| 134 | |
| 135 | /** |
| 136 | * Chainable mutator which adds list parameters with a common type |
| 137 | * |
| 138 | * @param string $listType One of the ListType constants |
| 139 | * @param (MessageParam|MessageSpecifier|string|int|float)[] ...$values Each value |
| 140 | * is an array of items suitable to pass as $params to ListParam::__construct() |
| 141 | * @return $this |
| 142 | */ |
| 143 | public function listParamsOfType( string $listType, ...$values ): MessageValue { |
| 144 | foreach ( $values as $value ) { |
| 145 | $this->params[] = new ListParam( $listType, $value ); |
| 146 | } |
| 147 | return $this; |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Chainable mutator which adds parameters of type text (ParamType::TEXT). |
| 152 | * |
| 153 | * @param MessageSpecifier|string|int|float ...$values |
| 154 | * @return $this |
| 155 | */ |
| 156 | public function textParams( ...$values ): MessageValue { |
| 157 | return $this->textParamsOfType( ParamType::TEXT, ...$values ); |
| 158 | } |
| 159 | |
| 160 | /** |
| 161 | * Chainable mutator which adds numeric parameters (ParamType::NUM). |
| 162 | * |
| 163 | * @param int|float ...$values |
| 164 | * @return $this |
| 165 | */ |
| 166 | public function numParams( ...$values ): MessageValue { |
| 167 | return $this->textParamsOfType( ParamType::NUM, ...$values ); |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Chainable mutator which adds parameters which are a duration specified |
| 172 | * in seconds (ParamType::DURATION_LONG). |
| 173 | * |
| 174 | * This is similar to shorDurationParams() except that the result will be |
| 175 | * more verbose. |
| 176 | * |
| 177 | * @param int|float ...$values |
| 178 | * @return $this |
| 179 | */ |
| 180 | public function longDurationParams( ...$values ): MessageValue { |
| 181 | return $this->textParamsOfType( ParamType::DURATION_LONG, ...$values ); |
| 182 | } |
| 183 | |
| 184 | /** |
| 185 | * Chainable mutator which adds parameters which are a duration specified |
| 186 | * in seconds (ParamType::DURATION_SHORT). |
| 187 | * |
| 188 | * This is similar to longDurationParams() except that the result will be more |
| 189 | * compact. |
| 190 | * |
| 191 | * @param int|float ...$values |
| 192 | * @return $this |
| 193 | */ |
| 194 | public function shortDurationParams( ...$values ): MessageValue { |
| 195 | return $this->textParamsOfType( ParamType::DURATION_SHORT, ...$values ); |
| 196 | } |
| 197 | |
| 198 | /** |
| 199 | * Chainable mutator which adds parameters which are an expiry timestamp (ParamType::EXPIRY). |
| 200 | * |
| 201 | * @param string ...$values Timestamp as accepted by the Wikimedia\Timestamp library, |
| 202 | * or "infinity" |
| 203 | * @return $this |
| 204 | */ |
| 205 | public function expiryParams( ...$values ): MessageValue { |
| 206 | return $this->textParamsOfType( ParamType::EXPIRY, ...$values ); |
| 207 | } |
| 208 | |
| 209 | /** |
| 210 | * Chainable mutator which adds parameters which are a date-time timestamp (ParamType::DATETIME). |
| 211 | * |
| 212 | * @since MediaWiki 1.36 |
| 213 | * @param string ...$values Timestamp as accepted by the Wikimedia\Timestamp library. |
| 214 | * @return $this |
| 215 | */ |
| 216 | public function dateTimeParams( ...$values ): MessageValue { |
| 217 | return $this->textParamsOfType( ParamType::DATETIME, ...$values ); |
| 218 | } |
| 219 | |
| 220 | /** |
| 221 | * Chainable mutator which adds parameters which are a date timestamp (ParamType::DATE). |
| 222 | * |
| 223 | * @since MediaWiki 1.36 |
| 224 | * @param string ...$values Timestamp as accepted by the Wikimedia\Timestamp library. |
| 225 | * @return $this |
| 226 | */ |
| 227 | public function dateParams( ...$values ): MessageValue { |
| 228 | return $this->textParamsOfType( ParamType::DATE, ...$values ); |
| 229 | } |
| 230 | |
| 231 | /** |
| 232 | * Chainable mutator which adds parameters which are a time timestamp (ParamType::TIME). |
| 233 | * |
| 234 | * @since MediaWiki 1.36 |
| 235 | * @param string ...$values Timestamp as accepted by the Wikimedia\Timestamp library. |
| 236 | * @return $this |
| 237 | */ |
| 238 | public function timeParams( ...$values ): MessageValue { |
| 239 | return $this->textParamsOfType( ParamType::TIME, ...$values ); |
| 240 | } |
| 241 | |
| 242 | /** |
| 243 | * Chainable mutator which adds parameters which are a user group (ParamType::GROUP). |
| 244 | * |
| 245 | * @since MediaWiki 1.38 |
| 246 | * @param string ...$values User Groups |
| 247 | * @return $this |
| 248 | */ |
| 249 | public function userGroupParams( ...$values ): MessageValue { |
| 250 | return $this->textParamsOfType( ParamType::GROUP, ...$values ); |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Chainable mutator which adds parameters which are a number of bytes (ParamType::SIZE). |
| 255 | * |
| 256 | * @param int ...$values |
| 257 | * @return $this |
| 258 | */ |
| 259 | public function sizeParams( ...$values ): MessageValue { |
| 260 | return $this->textParamsOfType( ParamType::SIZE, ...$values ); |
| 261 | } |
| 262 | |
| 263 | /** |
| 264 | * Chainable mutator which adds parameters which are a number of bits per |
| 265 | * second (ParamType::BITRATE). |
| 266 | * |
| 267 | * @param int|float ...$values |
| 268 | * @return $this |
| 269 | */ |
| 270 | public function bitrateParams( ...$values ): MessageValue { |
| 271 | return $this->textParamsOfType( ParamType::BITRATE, ...$values ); |
| 272 | } |
| 273 | |
| 274 | /** |
| 275 | * Chainable mutator which adds "raw" parameters (ParamType::RAW). |
| 276 | * |
| 277 | * Raw parameters are substituted after formatter processing. The caller is responsible |
| 278 | * for ensuring that the value will be safe for the intended output format, and |
| 279 | * documenting what that intended output format is. |
| 280 | * |
| 281 | * @param string ...$values |
| 282 | * @return $this |
| 283 | */ |
| 284 | public function rawParams( ...$values ): MessageValue { |
| 285 | return $this->textParamsOfType( ParamType::RAW, ...$values ); |
| 286 | } |
| 287 | |
| 288 | /** |
| 289 | * Chainable mutator which adds plaintext parameters (ParamType::PLAINTEXT). |
| 290 | * |
| 291 | * Plaintext parameters are substituted after formatter processing. The value |
| 292 | * will be escaped by the formatter as appropriate for the target output format |
| 293 | * so as to be represented as plain text rather than as any sort of markup. |
| 294 | * |
| 295 | * @param string ...$values |
| 296 | * @return $this |
| 297 | */ |
| 298 | public function plaintextParams( ...$values ): MessageValue { |
| 299 | return $this->textParamsOfType( ParamType::PLAINTEXT, ...$values ); |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Chainable mutator which adds comma lists (ListType::COMMA). |
| 304 | * |
| 305 | * The list parameters thus created are formatted as a comma-separated list, |
| 306 | * or some local equivalent. |
| 307 | * |
| 308 | * @param (MessageParam|MessageSpecifier|string|int|float)[] ...$values Each value |
| 309 | * is an array of items suitable to pass as $params to ListParam::__construct() |
| 310 | * @return $this |
| 311 | */ |
| 312 | public function commaListParams( ...$values ): MessageValue { |
| 313 | return $this->listParamsOfType( ListType::COMMA, ...$values ); |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Chainable mutator which adds semicolon lists (ListType::SEMICOLON). |
| 318 | * |
| 319 | * The list parameters thus created are formatted as a semicolon-separated |
| 320 | * list, or some local equivalent. |
| 321 | * |
| 322 | * @param (MessageParam|MessageSpecifier|string|int|float)[] ...$values Each value |
| 323 | * is an array of items suitable to pass as $params to ListParam::__construct() |
| 324 | * @return $this |
| 325 | */ |
| 326 | public function semicolonListParams( ...$values ): MessageValue { |
| 327 | return $this->listParamsOfType( ListType::SEMICOLON, ...$values ); |
| 328 | } |
| 329 | |
| 330 | /** |
| 331 | * Chainable mutator which adds pipe lists (ListType::PIPE). |
| 332 | * |
| 333 | * The list parameters thus created are formatted as a pipe ("|") -separated |
| 334 | * list, or some local equivalent. |
| 335 | * |
| 336 | * @param (MessageParam|MessageSpecifier|string|int|float)[] ...$values Each value |
| 337 | * is an array of items suitable to pass as $params to ListParam::__construct() |
| 338 | * @return $this |
| 339 | */ |
| 340 | public function pipeListParams( ...$values ): MessageValue { |
| 341 | return $this->listParamsOfType( ListType::PIPE, ...$values ); |
| 342 | } |
| 343 | |
| 344 | /** |
| 345 | * Chainable mutator which adds natural-language lists (ListType::AND). |
| 346 | * |
| 347 | * The list parameters thus created, when formatted, are joined as in natural |
| 348 | * language. In English, this means a comma-separated list, with the last |
| 349 | * two elements joined with "and". |
| 350 | * |
| 351 | * @param (MessageParam|string)[] ...$values |
| 352 | * @return $this |
| 353 | */ |
| 354 | public function textListParams( ...$values ): MessageValue { |
| 355 | return $this->listParamsOfType( ListType::AND, ...$values ); |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Dump the object for testing/debugging |
| 360 | * |
| 361 | * @return string |
| 362 | */ |
| 363 | public function dump(): string { |
| 364 | $contents = ''; |
| 365 | foreach ( $this->params as $param ) { |
| 366 | $contents .= $param->dump(); |
| 367 | } |
| 368 | return '<message key="' . htmlspecialchars( $this->key ) . '">' . |
| 369 | $contents . '</message>'; |
| 370 | } |
| 371 | |
| 372 | public function isSameAs( MessageValue $mv ): bool { |
| 373 | return $this->key === $mv->key && |
| 374 | count( $this->params ) === count( $mv->params ) && |
| 375 | array_all( |
| 376 | $this->params, |
| 377 | static fn ( $v, $k ) => $v->isSameAs( $mv->params[$k] ) |
| 378 | ); |
| 379 | } |
| 380 | |
| 381 | public function toJsonArray(): array { |
| 382 | // WARNING: When changing how this class is serialized, follow the instructions |
| 383 | // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>! |
| 384 | return [ |
| 385 | 'key' => $this->key, |
| 386 | 'params' => array_map( |
| 387 | /** |
| 388 | * Serialize trivial parameters as scalar values to minimize the footprint. Full |
| 389 | * round-trip compatibility is guaranteed via the constructor and {@see params}. |
| 390 | */ |
| 391 | static fn ( $p ) => $p->getType() === ParamType::TEXT ? $p->getValue() : $p, |
| 392 | $this->params |
| 393 | ), |
| 394 | ]; |
| 395 | } |
| 396 | |
| 397 | public static function newFromJsonArray( array $json ): MessageValue { |
| 398 | // WARNING: When changing how this class is serialized, follow the instructions |
| 399 | // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>! |
| 400 | return new self( $json['key'], $json['params'] ); |
| 401 | } |
| 402 | } |