MediaWiki master
StatusValue.php
Go to the documentation of this file.
1<?php
23use Wikimedia\Assert\Assert;
25
50
55 protected $ok = true;
56
63 protected $errors = [];
64
66 public $value;
67
69 public $success = [];
70
72 public $successCount = 0;
73
75 public $failCount = 0;
76
79
87 public static function newFatal( $message, ...$parameters ) {
88 $result = new static();
89 $result->fatal( $message, ...$parameters );
90 return $result;
91 }
92
99 public static function newGood( $value = null ) {
100 $result = new static();
101 $result->value = $value;
102 return $result;
103 }
104
116 public function splitByErrorType() {
117 $errorsOnlyStatusValue = static::newGood();
118 $warningsOnlyStatusValue = static::newGood();
119 $warningsOnlyStatusValue->setResult( true, $this->getValue() );
120 $errorsOnlyStatusValue->setResult( $this->isOK(), $this->getValue() );
121
122 foreach ( $this->errors as $item ) {
123 if ( $item['type'] === 'warning' ) {
124 $warningsOnlyStatusValue->errors[] = $item;
125 } else {
126 $errorsOnlyStatusValue->errors[] = $item;
127 }
128 }
129
130 return [ $errorsOnlyStatusValue, $warningsOnlyStatusValue ];
131 }
132
139 public function isGood() {
140 return $this->ok && !$this->errors;
141 }
142
148 public function isOK() {
149 return $this->ok;
150 }
151
155 public function getValue() {
156 return $this->value;
157 }
158
168 public function getErrors() {
169 return $this->errors;
170 }
171
178 public function setOK( $ok ) {
179 $this->ok = $ok;
180 return $this;
181 }
182
190 public function setResult( $ok, $value = null ) {
191 $this->ok = (bool)$ok;
192 $this->value = $value;
193 return $this;
194 }
195
213 private function addError( array $newError ) {
214 if ( $newError[ 'message' ] instanceof MessageSpecifier ) {
215 if ( $newError['params'] ) {
216 // Deprecate code like `Status::newFatal( wfMessage( 'foo' ), 'param' )`
217 // - the parameters have always been ignored, so this is usually a mistake.
218 wfDeprecatedMsg( 'Combining MessageSpecifier and parameters array' .
219 ' was deprecated in MediaWiki 1.43', '1.43' );
220 }
221
222 $isEqual = static function ( $key, $params ) use ( $newError ) {
223 if ( $key instanceof MessageSpecifier ) {
224 // compare attributes of both MessageSpecifiers
225 return $newError['message'] == $key;
226 } else {
227 return $newError['message']->getKey() === $key &&
228 $newError['message']->getParams() === $params;
229 }
230 };
231 } else {
232 $isEqual = static function ( $key, $params ) use ( $newError ) {
233 if ( $key instanceof MessageSpecifier ) {
234 $params = $key->getParams();
235 $key = $key->getKey();
236 }
237 return $newError['message'] === $key && $newError['params'] === $params;
238 };
239 }
240 foreach ( $this->errors as [ 'type' => &$type, 'message' => $key, 'params' => $params ] ) {
241 if ( $isEqual( $key, $params ) ) {
242 if ( $type === 'warning' && $newError['type'] === 'error' ) {
243 $type = 'error';
244 }
245 return $this;
246 }
247 }
248 $this->errors[] = $newError;
249 return $this;
250 }
251
259 public function warning( $message, ...$parameters ) {
260 $message = $this->normalizeMessage( $message );
261
262 return $this->addError( [
263 'type' => 'warning',
264 'message' => $message,
265 'params' => $parameters
266 ] );
267 }
268
277 public function error( $message, ...$parameters ) {
278 $message = $this->normalizeMessage( $message );
279
280 return $this->addError( [
281 'type' => 'error',
282 'message' => $message,
283 'params' => $parameters
284 ] );
285 }
286
295 public function fatal( $message, ...$parameters ) {
296 $this->ok = false;
297 return $this->error( $message, ...$parameters );
298 }
299
307 public function merge( $other, $overwriteValue = false ) {
308 if ( $this->statusData !== null && $other->statusData !== null ) {
309 throw new RuntimeException( "Status cannot be merged, because they both have \$statusData" );
310 } else {
311 $this->statusData ??= $other->statusData;
312 }
313
314 foreach ( $other->errors as $error ) {
315 $this->addError( $error );
316 }
317 $this->ok = $this->ok && $other->ok;
318 if ( $overwriteValue ) {
319 $this->value = $other->value;
320 }
321 $this->successCount += $other->successCount;
322 $this->failCount += $other->failCount;
323
324 return $this;
325 }
326
339 public function getErrorsByType( $type ) {
340 $result = [];
341 foreach ( $this->errors as $error ) {
342 if ( $error['type'] === $type ) {
343 $result[] = $error;
344 }
345 }
346
347 return $result;
348 }
349
358 public function getMessages( ?string $type = null ): array {
359 Assert::parameter( $type === null || $type === 'warning' || $type === 'error',
360 '$type', "must be null, 'warning', or 'error'" );
361 $result = [];
362 foreach ( $this->errors as $error ) {
363 if ( $type === null || $error['type'] === $type ) {
364 [ 'message' => $key, 'params' => $params ] = $error;
365 if ( $key instanceof MessageSpecifier ) {
366 $result[] = $key;
367 } else {
368 // TODO: Make MessageValue implement MessageSpecifier, and use a MessageValue here
369 $result[] = new Message( $key, $params );
370 }
371 }
372 }
373
374 return $result;
375 }
376
385 public function hasMessage( $message ) {
386 if ( $message instanceof MessageSpecifier ) {
387 wfDeprecatedMsg( 'Passing MessageSpecifier to hasMessage()' .
388 ' was deprecated in MediaWiki 1.43', '1.43' );
389 $message = $message->getKey();
390 } elseif ( $message instanceof MessageValue ) {
391 wfDeprecatedMsg( 'Passing MessageValue to hasMessage()' .
392 ' was deprecated in MediaWiki 1.43', '1.43' );
393 $message = $message->getKey();
394 }
395
396 foreach ( $this->errors as [ 'message' => $key ] ) {
397 if ( ( $key instanceof MessageSpecifier && $key->getKey() === $message ) ||
398 $key === $message
399 ) {
400 return true;
401 }
402 }
403
404 return false;
405 }
406
415 public function hasMessagesExcept( ...$messages ) {
416 $exceptedKeys = [];
417 foreach ( $messages as $message ) {
418 if ( $message instanceof MessageSpecifier ) {
419 wfDeprecatedMsg( 'Passing MessageSpecifier to hasMessagesExcept()' .
420 ' was deprecated in MediaWiki 1.43', '1.43' );
421 $message = $message->getKey();
422 } elseif ( $message instanceof MessageValue ) {
423 wfDeprecatedMsg( 'Passing MessageValue to hasMessagesExcept()' .
424 ' was deprecated in MediaWiki 1.43', '1.43' );
425 $message = $message->getKey();
426 }
427 $exceptedKeys[] = $message;
428 }
429
430 foreach ( $this->errors as [ 'message' => $key ] ) {
431 if ( $key instanceof MessageSpecifier ) {
432 $key = $key->getKey();
433 }
434 if ( !in_array( $key, $exceptedKeys, true ) ) {
435 return true;
436 }
437 }
438
439 return false;
440 }
441
463 public function replaceMessage( $source, $dest ) {
464 $replaced = false;
465
466 if ( $source instanceof MessageSpecifier ) {
467 wfDeprecatedMsg( 'Passing MessageSpecifier as $source to replaceMessage()' .
468 ' was deprecated in MediaWiki 1.43', '1.43' );
469 } elseif ( $source instanceof MessageValue ) {
470 wfDeprecatedMsg( 'Passing MessageValue as $source to replaceMessage()' .
471 ' was deprecated in MediaWiki 1.43', '1.43' );
472 $source = $this->normalizeMessage( $source );
473 }
474
475 $dest = $this->normalizeMessage( $dest );
476
477 foreach ( $this->errors as [ 'message' => &$message, 'params' => &$params ] ) {
478 if ( $message === $source ||
479 ( $message instanceof MessageSpecifier && $message->getKey() === $source )
480 ) {
481 $message = $dest;
482 if ( $dest instanceof MessageSpecifier ) {
483 // 'params' will be ignored now, so remove them from the internal array
484 $params = [];
485 }
486 $replaced = true;
487 }
488 }
489
490 return $replaced;
491 }
492
499 public function __toString() {
500 $status = $this->isOK() ? "OK" : "Error";
501 if ( count( $this->errors ) ) {
502 $errorcount = "collected " . ( count( $this->errors ) ) . " message(s) on the way";
503 } else {
504 $errorcount = "no errors detected";
505 }
506 if ( isset( $this->value ) ) {
507 $valstr = gettype( $this->value ) . " value set";
508 if ( is_object( $this->value ) ) {
509 $valstr .= "\"" . get_class( $this->value ) . "\" instance";
510 }
511 } else {
512 $valstr = "no value set";
513 }
514 $out = sprintf( "<%s, %s, %s>",
515 $status,
516 $errorcount,
517 $valstr
518 );
519 if ( count( $this->errors ) > 0 ) {
520 $hdr = sprintf( "+-%'-8s-+-%'-25s-+-%'-36s-+\n", "", "", "" );
521 $out .= "\n" . $hdr;
522 foreach ( $this->errors as [ 'type' => $type, 'message' => $key, 'params' => $params ] ) {
523 if ( $key instanceof MessageSpecifier ) {
524 $params = $key->getParams();
525 $key = $key->getKey();
526 }
527
528 $keyChunks = mb_str_split( $key, 25 );
529 $paramsChunks = mb_str_split( $this->flattenParams( $params, " | " ), 36 );
530
531 // array_map(null,...) is like Python's zip()
532 foreach ( array_map( null, [ $type ], $keyChunks, $paramsChunks )
533 as [ $typeChunk, $keyChunk, $paramsChunk ]
534 ) {
535 $out .= sprintf( "| %-8s | %-25s | %-36s |\n",
536 $typeChunk,
537 $keyChunk,
538 $paramsChunk
539 );
540 }
541 }
542 $out .= $hdr;
543 }
544
545 return $out;
546 }
547
554 private function flattenParams( array $params, string $joiner = ', ' ): string {
555 $ret = [];
556 foreach ( $params as $p ) {
557 if ( is_array( $p ) ) {
558 $r = '[ ' . self::flattenParams( $p ) . ' ]';
559 } elseif ( $p instanceof MessageSpecifier ) {
560 $r = '{ ' . $p->getKey() . ': ' . self::flattenParams( $p->getParams() ) . ' }';
561 } else {
562 $r = (string)$p;
563 }
564
565 $ret[] = mb_strlen( $r ) > 100 ? mb_substr( $r, 0, 99 ) . "..." : $r;
566 }
567 return implode( $joiner, $ret );
568 }
569
579 protected function getStatusArray( $type = false ) {
580 $result = [];
581
582 foreach ( $this->getErrors() as $error ) {
583 if ( !$type || $error['type'] === $type ) {
584 if ( $error['message'] instanceof MessageSpecifier ) {
585 $result[] = [ $error['message']->getKey(), ...$error['message']->getParams() ];
586 } else {
587 $result[] = [ $error['message'], ...$error['params'] ];
588 }
589 }
590 }
591
592 return $result;
593 }
594
600 private function normalizeMessage( $message ) {
601 if ( $message instanceof MessageValue ) {
602 $converter = new Converter();
603 return $converter->convertMessageValue( $message );
604 }
605
606 return $message;
607 }
608}
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
array $params
The job parameters.
Converter between Message and MessageValue.
Definition Converter.php:18
The Message class deals with fetching and processing of interface message into a variety of formats.
Definition Message.php:158
Generic operation result class Has warning/error list, boolean status and arbitrary value.
hasMessage( $message)
Returns true if the specified message is present as a warning or error.
static newFatal( $message,... $parameters)
Factory function for fatal errors.
int $failCount
Counter for batch operations.
array[] $errors
getErrors()
Get the list of errors.
getMessages(?string $type=null)
Returns a list of error messages, optionally only those of the given type.
replaceMessage( $source, $dest)
If the specified source message exists, replace it with the specified destination message,...
splitByErrorType()
Splits this StatusValue object into two new StatusValue objects, one which contains only the error me...
setOK( $ok)
Change operation status.
hasMessagesExcept(... $messages)
Returns true if any other message than the specified ones is present as a warning or error.
getStatusArray( $type=false)
Returns a list of status messages of the given type (or all if false)
isOK()
Returns whether the operation completed.
fatal( $message,... $parameters)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
setResult( $ok, $value=null)
Change operation result.
merge( $other, $overwriteValue=false)
Merge another status object into this one.
mixed $statusData
arbitrary extra data about the operation
__toString()
Returns a string representation of the status for debugging.
error( $message,... $parameters)
Add an error, do not set fatal flag This can be used for non-fatal errors.
getErrorsByType( $type)
Returns a list of status messages of the given type.
warning( $message,... $parameters)
Add a new warning.
isGood()
Returns whether the operation completed and didn't have any error or warnings.
static newGood( $value=null)
Factory function for good results.
int $successCount
Counter for batch operations.
Value object representing a message for i18n.
getKey()
Returns the message key.
$source