MediaWiki  master
ApiErrorFormatter.php
Go to the documentation of this file.
1 <?php
26 
36  private static $dummyTitle = null;
37 
39  protected $result;
40 
42  protected $lang;
44  private $title = null;
45  protected $useDB = false;
46  protected $format = 'none';
47 
60  public function __construct( ApiResult $result, Language $lang, $format, $useDB = false ) {
61  $this->result = $result;
62  $this->lang = $lang;
63  $this->useDB = $useDB;
64  $this->format = $format;
65  }
66 
79  public static function isValidApiCode( $code ) {
80  return is_string( $code ) && (
81  preg_match( '/^[a-zA-Z0-9_-]+$/', $code ) ||
82  // TODO: Deprecate this
83  preg_match( '/^internal_api_error_[^\0\r\n]+$/', $code )
84  );
85  }
86 
94  public function newWithFormat( $format ) {
95  return new self( $this->result, $this->lang, $format, $this->useDB );
96  }
97 
103  public function getFormat() {
104  return $this->format;
105  }
106 
112  public function getLanguage() {
113  return $this->lang;
114  }
115 
120  protected function getDummyTitle(): PageReference {
121  if ( self::$dummyTitle === null ) {
122  self::$dummyTitle = PageReferenceValue::localReference(
123  NS_SPECIAL,
124  'Badtitle/' . __METHOD__
125  );
126  }
127  return self::$dummyTitle;
128  }
129 
135  public function getContextTitle(): PageReference {
136  return $this->title ?: $this->getDummyTitle();
137  }
138 
144  public function setContextTitle( PageReference $title ) {
145  $this->title = $title;
146  }
147 
155  public function addWarning( $modulePath, $msg, $code = null, $data = null ) {
156  $msg = ApiMessage::create( $msg, $code, $data )
157  ->inLanguage( $this->lang )
158  ->page( $this->getContextTitle() )
159  ->useDatabase( $this->useDB );
160  $this->addWarningOrError( 'warning', $modulePath, $msg );
161  }
162 
170  public function addError( $modulePath, $msg, $code = null, $data = null ) {
171  $msg = ApiMessage::create( $msg, $code, $data )
172  ->inLanguage( $this->lang )
173  ->page( $this->getContextTitle() )
174  ->useDatabase( $this->useDB );
175  $this->addWarningOrError( 'error', $modulePath, $msg );
176  }
177 
185  public function addMessagesFromStatus(
186  $modulePath, StatusValue $status, $types = [ 'warning', 'error' ], array $filter = []
187  ) {
188  if ( $status->isGood() || !$status->getErrors() ) {
189  return;
190  }
191 
192  $types = (array)$types;
193  foreach ( $status->getErrors() as $error ) {
194  if ( !in_array( $error['type'], $types, true ) ) {
195  continue;
196  }
197 
198  if ( $error['type'] === 'error' ) {
199  $tag = 'error';
200  } else {
201  // Assume any unknown type is a warning
202  $tag = 'warning';
203  }
204 
205  $msg = ApiMessage::create( $error )
206  ->inLanguage( $this->lang )
207  ->page( $this->getContextTitle() )
208  ->useDatabase( $this->useDB );
209  if ( !in_array( $msg->getKey(), $filter, true ) ) {
210  $this->addWarningOrError( $tag, $modulePath, $msg );
211  }
212  }
213  }
214 
227  public function getMessageFromException( Throwable $exception, array $options = [] ) {
228  $options += [ 'code' => null, 'data' => [] ];
229 
230  if ( $exception instanceof ILocalizedException ) {
231  $msg = $exception->getMessageObject();
232  $params = [];
233  } elseif ( $exception instanceof MessageSpecifier ) {
234  $msg = Message::newFromSpecifier( $exception );
235  $params = [];
236  } else {
237  if ( isset( $options['wrap'] ) ) {
238  $msg = $options['wrap'];
239  } else {
240  $msg = new RawMessage( '$1' );
241  if ( !isset( $options['code'] ) ) {
242  $class = preg_replace( '#^Wikimedia\\\Rdbms\\\#', '', get_class( $exception ) );
243  $options['code'] = 'internal_api_error_' . $class;
244  $options['data']['errorclass'] = get_class( $exception );
245  }
246  }
247  $params = [ wfEscapeWikiText( $exception->getMessage() ) ];
248  }
249  return ApiMessage::create( $msg, $options['code'], $options['data'] )
250  ->params( $params )
251  ->inLanguage( $this->lang )
252  ->page( $this->getContextTitle() )
253  ->useDatabase( $this->useDB );
254  }
255 
264  public function formatException( Throwable $exception, array $options = [] ) {
265  return $this->formatMessage(
266  // @phan-suppress-next-line PhanTypeMismatchArgument
267  $this->getMessageFromException( $exception, $options ),
268  $options['format'] ?? null
269  );
270  }
271 
278  public function formatMessage( $msg, $format = null ) {
279  $msg = ApiMessage::create( $msg )
280  ->inLanguage( $this->lang )
281  ->page( $this->getContextTitle() )
282  ->useDatabase( $this->useDB );
283  return $this->formatMessageInternal( $msg, $format ?: $this->format );
284  }
285 
293  public function arrayFromStatus( StatusValue $status, $type = 'error', $format = null ) {
294  if ( $status->isGood() || !$status->getErrors() ) {
295  return [];
296  }
297 
298  $result = new ApiResult( 1000000 );
299  $formatter = new ApiErrorFormatter(
300  $result, $this->lang, $format ?: $this->format, $this->useDB
301  );
302  $formatter->addMessagesFromStatus( null, $status, [ $type ] );
303  switch ( $type ) {
304  case 'error':
305  return (array)$result->getResultData( [ 'errors' ] );
306  case 'warning':
307  return (array)$result->getResultData( [ 'warnings' ] );
308  }
309  }
310 
317  public static function stripMarkup( $text ) {
318  // Turn semantic quoting tags to quotes
319  $ret = preg_replace( '!</?(var|kbd|samp|code)>!', '"', $text );
320 
321  // Strip tags and decode.
322  $ret = Sanitizer::stripAllTags( $ret );
323 
324  return $ret;
325  }
326 
332  private function formatRawMessage( MessageSpecifier $msg ) {
333  $ret = [
334  'key' => $msg->getKey(),
335  'params' => $msg->getParams(),
336  ];
337  ApiResult::setIndexedTagName( $ret['params'], 'param' );
338 
339  // Transform Messages as parameters in the style of Message::fooParam().
340  foreach ( $ret['params'] as $i => $param ) {
341  if ( $param instanceof MessageSpecifier ) {
342  $ret['params'][$i] = [ 'message' => $this->formatRawMessage( $param ) ];
343  }
344  }
345  return $ret;
346  }
347 
355  protected function formatMessageInternal( $msg, $format ) {
356  $value = [ 'code' => $msg->getApiCode() ];
357  switch ( $format ) {
358  case 'plaintext':
359  $value += [
360  'text' => self::stripMarkup( $msg->text() ),
361  ApiResult::META_CONTENT => 'text',
362  ];
363  break;
364 
365  case 'wikitext':
366  $value += [
367  'text' => $msg->text(),
368  ApiResult::META_CONTENT => 'text',
369  ];
370  break;
371 
372  case 'html':
373  $value += [
374  'html' => $msg->parse(),
375  ApiResult::META_CONTENT => 'html',
376  ];
377  break;
378 
379  case 'raw':
380  $value += $this->formatRawMessage( $msg );
381  break;
382 
383  case 'none':
384  break;
385  }
386  $data = $msg->getApiData();
387  if ( $data ) {
388  $value['data'] = $msg->getApiData() + [
389  ApiResult::META_TYPE => 'assoc',
390  ];
391  }
392  return $value;
393  }
394 
401  protected function addWarningOrError( $tag, $modulePath, $msg ) {
402  $value = $this->formatMessageInternal( $msg, $this->format );
403  if ( $modulePath !== null ) {
404  $value += [ 'module' => $modulePath ];
405  }
406 
407  $path = [ $tag . 's' ];
408  $existing = $this->result->getResultData( $path );
409  if ( $existing === null || !in_array( $value, $existing ) ) {
410  $flags = ApiResult::NO_SIZE_CHECK;
411  if ( $existing === null ) {
412  $flags |= ApiResult::ADD_ON_TOP;
413  }
414  $this->result->addValue( $path, null, $value, $flags );
415  $this->result->addIndexedTagName( $path, $tag );
416  }
417  }
418 }
Message\newFromSpecifier
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition: Message.php:398
ApiErrorFormatter\getLanguage
getLanguage()
Fetch the Language for this formatter.
Definition: ApiErrorFormatter.php:112
StatusValue
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: StatusValue.php:43
ApiErrorFormatter\getFormat
getFormat()
Fetch the format for this formatter.
Definition: ApiErrorFormatter.php:103
ApiErrorFormatter\addError
addError( $modulePath, $msg, $code=null, $data=null)
Add an error to the result.
Definition: ApiErrorFormatter.php:170
ApiErrorFormatter\isValidApiCode
static isValidApiCode( $code)
Test whether a code is a valid API error code.
Definition: ApiErrorFormatter.php:79
Sanitizer\stripAllTags
static stripAllTags( $html)
Take a fragment of (potentially invalid) HTML and return a version with any tags removed,...
Definition: Sanitizer.php:1577
ApiResult\META_TYPE
const META_TYPE
Key for the 'type' metadata item.
Definition: ApiResult.php:110
MessageSpecifier\getKey
getKey()
Returns the message key.
MessageSpecifier
Definition: MessageSpecifier.php:24
ApiErrorFormatter\setContextTitle
setContextTitle(PageReference $title)
Set the page used for rendering error messages, e.g.
Definition: ApiErrorFormatter.php:144
ApiResult\NO_SIZE_CHECK
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition: ApiResult.php:58
ApiErrorFormatter\addWarning
addWarning( $modulePath, $msg, $code=null, $data=null)
Add a warning to the result.
Definition: ApiErrorFormatter.php:155
Page\PageReference
Interface for objects (potentially) representing a page that can be viewable and linked to on a wiki.
Definition: PageReference.php:49
MessageSpecifier\getParams
getParams()
Returns the message parameters.
NS_SPECIAL
const NS_SPECIAL
Definition: Defines.php:53
ApiErrorFormatter\formatMessageInternal
formatMessageInternal( $msg, $format)
Format a message as an array.
Definition: ApiErrorFormatter.php:355
StatusValue\isGood
isGood()
Returns whether the operation completed and didn't have any error or warnings.
Definition: StatusValue.php:122
ApiErrorFormatter\$lang
Language $lang
Definition: ApiErrorFormatter.php:42
ApiErrorFormatter\$result
ApiResult $result
Definition: ApiErrorFormatter.php:39
ApiErrorFormatter\stripMarkup
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
Definition: ApiErrorFormatter.php:317
ApiResult
This class represents the result of the API operations.
Definition: ApiResult.php:35
ApiErrorFormatter\addWarningOrError
addWarningOrError( $tag, $modulePath, $msg)
Actually add the warning or error to the result.
Definition: ApiErrorFormatter.php:401
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
ApiErrorFormatter\formatRawMessage
formatRawMessage(MessageSpecifier $msg)
Format a Message object for raw format.
Definition: ApiErrorFormatter.php:332
ILocalizedException
Interface for MediaWiki-localized exceptions.
Definition: ILocalizedException.php:29
ApiErrorFormatter\__construct
__construct(ApiResult $result, Language $lang, $format, $useDB=false)
Definition: ApiErrorFormatter.php:60
ApiErrorFormatter\$title
PageReference null $title
page used for rendering error messages, or null to use the dummy title
Definition: ApiErrorFormatter.php:44
ApiErrorFormatter\$format
$format
Definition: ApiErrorFormatter.php:46
ApiErrorFormatter\$dummyTitle
static PageReference $dummyTitle
Dummy title to silence warnings from MessageCache::parse()
Definition: ApiErrorFormatter.php:36
ApiErrorFormatter\newWithFormat
newWithFormat( $format)
Return a formatter like this one but with a different format.
Definition: ApiErrorFormatter.php:94
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:603
ApiErrorFormatter\formatMessage
formatMessage( $msg, $format=null)
Format a message as an array.
Definition: ApiErrorFormatter.php:278
StatusValue\getErrors
getErrors()
Get the list of errors.
Definition: StatusValue.php:149
ApiErrorFormatter\formatException
formatException(Throwable $exception, array $options=[])
Format a throwable as an array.
Definition: ApiErrorFormatter.php:264
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1456
ApiErrorFormatter\getContextTitle
getContextTitle()
Get the page used for rendering error messages, e.g.
Definition: ApiErrorFormatter.php:135
ApiErrorFormatter
Formats errors and warnings for the API, and add them to the associated ApiResult.
Definition: ApiErrorFormatter.php:34
ApiResult\ADD_ON_TOP
const ADD_ON_TOP
For addValue(), setValue() and similar functions, if the value does not exist, add it as the first el...
Definition: ApiResult.php:49
ApiErrorFormatter\getMessageFromException
getMessageFromException(Throwable $exception, array $options=[])
Get an ApiMessage from a throwable.
Definition: ApiErrorFormatter.php:227
ApiResult\getResultData
getResultData( $path=[], $transforms=[])
Get the result data array.
Definition: ApiResult.php:240
Page\PageReferenceValue
Immutable value object representing a page reference.
Definition: PageReferenceValue.php:42
ApiErrorFormatter\addMessagesFromStatus
addMessagesFromStatus( $modulePath, StatusValue $status, $types=[ 'warning', 'error'], array $filter=[])
Add warnings and errors from a StatusValue object to the result.
Definition: ApiErrorFormatter.php:185
$path
$path
Definition: NoLocalSettings.php:25
ApiErrorFormatter\arrayFromStatus
arrayFromStatus(StatusValue $status, $type='error', $format=null)
Format messages from a StatusValue as an array.
Definition: ApiErrorFormatter.php:293
RawMessage
Variant of the Message class.
Definition: RawMessage.php:35
ApiResult\META_CONTENT
const META_CONTENT
Key for the 'content' metadata item.
Definition: ApiResult.php:90
ApiErrorFormatter\getDummyTitle
getDummyTitle()
Fetch a dummy title to set on Messages.
Definition: ApiErrorFormatter.php:120
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
ApiErrorFormatter\$useDB
$useDB
Definition: ApiErrorFormatter.php:45
$type
$type
Definition: testCompression.php:52