MediaWiki  master
ApiErrorFormatter.php
Go to the documentation of this file.
1 <?php
33  private static $dummyTitle = null;
34 
36  protected $result;
37 
39  protected $lang;
40  protected $useDB = false;
41  protected $format = 'none';
42 
55  public function __construct( ApiResult $result, Language $lang, $format, $useDB = false ) {
56  $this->result = $result;
57  $this->lang = $lang;
58  $this->useDB = $useDB;
59  $this->format = $format;
60  }
61 
74  public static function isValidApiCode( $code ) {
75  return is_string( $code ) && (
76  preg_match( '/^[a-zA-Z0-9_-]+$/', $code ) ||
77  // TODO: Deprecate this
78  preg_match( '/^internal_api_error_[^\0\r\n]+$/', $code )
79  );
80  }
81 
89  public function newWithFormat( $format ) {
90  return new self( $this->result, $this->lang, $format, $this->useDB );
91  }
92 
98  public function getFormat() {
99  return $this->format;
100  }
101 
107  public function getLanguage() {
108  return $this->lang;
109  }
110 
115  protected function getDummyTitle() {
116  if ( self::$dummyTitle === null ) {
117  self::$dummyTitle = Title::makeTitle( NS_SPECIAL, 'Badtitle/' . __METHOD__ );
118  }
119  return self::$dummyTitle;
120  }
121 
129  public function addWarning( $modulePath, $msg, $code = null, $data = null ) {
130  $msg = ApiMessage::create( $msg, $code, $data )
131  ->inLanguage( $this->lang )
132  ->title( $this->getDummyTitle() )
133  ->useDatabase( $this->useDB );
134  $this->addWarningOrError( 'warning', $modulePath, $msg );
135  }
136 
144  public function addError( $modulePath, $msg, $code = null, $data = null ) {
145  $msg = ApiMessage::create( $msg, $code, $data )
146  ->inLanguage( $this->lang )
147  ->title( $this->getDummyTitle() )
148  ->useDatabase( $this->useDB );
149  $this->addWarningOrError( 'error', $modulePath, $msg );
150  }
151 
159  public function addMessagesFromStatus(
160  $modulePath, StatusValue $status, $types = [ 'warning', 'error' ], array $filter = []
161  ) {
162  if ( $status->isGood() || !$status->getErrors() ) {
163  return;
164  }
165 
166  $types = (array)$types;
167  foreach ( $status->getErrors() as $error ) {
168  if ( !in_array( $error['type'], $types, true ) ) {
169  continue;
170  }
171 
172  if ( $error['type'] === 'error' ) {
173  $tag = 'error';
174  } else {
175  // Assume any unknown type is a warning
176  $tag = 'warning';
177  }
178 
179  $msg = ApiMessage::create( $error )
180  ->inLanguage( $this->lang )
181  ->title( $this->getDummyTitle() )
182  ->useDatabase( $this->useDB );
183  if ( !in_array( $msg->getKey(), $filter, true ) ) {
184  $this->addWarningOrError( $tag, $modulePath, $msg );
185  }
186  }
187  }
188 
201  public function getMessageFromException( $exception, array $options = [] ) {
202  $options += [ 'code' => null, 'data' => [] ];
203 
204  if ( $exception instanceof ILocalizedException ) {
205  $msg = $exception->getMessageObject();
206  $params = [];
207  } elseif ( $exception instanceof MessageSpecifier ) {
208  $msg = Message::newFromSpecifier( $exception );
209  $params = [];
210  } else {
211  if ( isset( $options['wrap'] ) ) {
212  $msg = $options['wrap'];
213  } else {
214  $msg = new RawMessage( '$1' );
215  if ( !isset( $options['code'] ) ) {
216  $class = preg_replace( '#^Wikimedia\\\Rdbms\\\#', '', get_class( $exception ) );
217  $options['code'] = 'internal_api_error_' . $class;
218  $options['data']['errorclass'] = get_class( $exception );
219  }
220  }
221  $params = [ wfEscapeWikiText( $exception->getMessage() ) ];
222  }
223  return ApiMessage::create( $msg, $options['code'], $options['data'] )
224  ->params( $params )
225  ->inLanguage( $this->lang )
226  ->title( $this->getDummyTitle() )
227  ->useDatabase( $this->useDB );
228  }
229 
238  public function formatException( $exception, array $options = [] ) {
239  return $this->formatMessage(
240  // @phan-suppress-next-line PhanTypeMismatchArgument
241  $this->getMessageFromException( $exception, $options ),
242  $options['format'] ?? null
243  );
244  }
245 
252  public function formatMessage( $msg, $format = null ) {
253  $msg = ApiMessage::create( $msg )
254  ->inLanguage( $this->lang )
255  ->title( $this->getDummyTitle() )
256  ->useDatabase( $this->useDB );
257  return $this->formatMessageInternal( $msg, $format ?: $this->format );
258  }
259 
267  public function arrayFromStatus( StatusValue $status, $type = 'error', $format = null ) {
268  if ( $status->isGood() || !$status->getErrors() ) {
269  return [];
270  }
271 
272  $result = new ApiResult( 1e6 );
273  $formatter = new ApiErrorFormatter(
274  $result, $this->lang, $format ?: $this->format, $this->useDB
275  );
276  $formatter->addMessagesFromStatus( null, $status, [ $type ] );
277  switch ( $type ) {
278  case 'error':
279  return (array)$result->getResultData( [ 'errors' ] );
280  case 'warning':
281  return (array)$result->getResultData( [ 'warnings' ] );
282  }
283  }
284 
291  public static function stripMarkup( $text ) {
292  // Turn semantic quoting tags to quotes
293  $ret = preg_replace( '!</?(var|kbd|samp|code)>!', '"', $text );
294 
295  // Strip tags and decode.
296  $ret = Sanitizer::stripAllTags( $ret );
297 
298  return $ret;
299  }
300 
306  private function formatRawMessage( MessageSpecifier $msg ) {
307  $ret = [
308  'key' => $msg->getKey(),
309  'params' => $msg->getParams(),
310  ];
311  ApiResult::setIndexedTagName( $ret['params'], 'param' );
312 
313  // Transform Messages as parameters in the style of Message::fooParam().
314  foreach ( $ret['params'] as $i => $param ) {
315  if ( $param instanceof MessageSpecifier ) {
316  $ret['params'][$i] = [ 'message' => $this->formatRawMessage( $param ) ];
317  }
318  }
319  return $ret;
320  }
321 
329  protected function formatMessageInternal( $msg, $format ) {
330  $value = [ 'code' => $msg->getApiCode() ];
331  switch ( $format ) {
332  case 'plaintext':
333  $value += [
334  'text' => self::stripMarkup( $msg->text() ),
335  ApiResult::META_CONTENT => 'text',
336  ];
337  break;
338 
339  case 'wikitext':
340  $value += [
341  'text' => $msg->text(),
342  ApiResult::META_CONTENT => 'text',
343  ];
344  break;
345 
346  case 'html':
347  $value += [
348  'html' => $msg->parse(),
349  ApiResult::META_CONTENT => 'html',
350  ];
351  break;
352 
353  case 'raw':
354  $value += $this->formatRawMessage( $msg );
355  break;
356 
357  case 'none':
358  break;
359  }
360  $data = $msg->getApiData();
361  if ( $data ) {
362  $value['data'] = $msg->getApiData() + [
363  ApiResult::META_TYPE => 'assoc',
364  ];
365  }
366  return $value;
367  }
368 
375  protected function addWarningOrError( $tag, $modulePath, $msg ) {
376  $value = $this->formatMessageInternal( $msg, $this->format );
377  if ( $modulePath !== null ) {
378  $value += [ 'module' => $modulePath ];
379  }
380 
381  $path = [ $tag . 's' ];
382  $existing = $this->result->getResultData( $path );
383  if ( $existing === null || !in_array( $value, $existing ) ) {
384  $flags = ApiResult::NO_SIZE_CHECK;
385  if ( $existing === null ) {
386  $flags |= ApiResult::ADD_ON_TOP;
387  }
388  $this->result->addValue( $path, null, $value, $flags );
389  $this->result->addIndexedTagName( $path, $tag );
390  }
391  }
392 }
formatMessage( $msg, $format=null)
Format a message as an array.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking, formatting, etc.
Formats errors and warnings for the API, and add them to the associated ApiResult.
static stripMarkup( $text)
Turn wikitext into something resembling plaintext.
getKey()
Returns the message key.
static isValidApiCode( $code)
Test whether a code is a valid API error code.
const NS_SPECIAL
Definition: Defines.php:49
const META_TYPE
Key for the &#39;type&#39; metadata item.
Definition: ApiResult.php:110
isGood()
Returns whether the operation completed and didn&#39;t have any error or warnings.
addError( $modulePath, $msg, $code=null, $data=null)
Add an error to the result.
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
addWarning( $modulePath, $msg, $code=null, $data=null)
Add a warning to the result.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
formatMessageInternal( $msg, $format)
Format a message as an array.
formatRawMessage(MessageSpecifier $msg)
Format a Message object for raw format.
static stripAllTags( $html)
Take a fragment of (potentially invalid) HTML and return a version with any tags removed, encoded as plain text.
Definition: Sanitizer.php:2003
addWarningOrError( $tag, $modulePath, $msg)
Actually add the warning or error to the result.
addMessagesFromStatus( $modulePath, StatusValue $status, $types=[ 'warning', 'error'], array $filter=[])
Add warnings and errors from a StatusValue object to the result.
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don&#39;t use this unless yo...
Definition: ApiResult.php:58
This class represents the result of the API operations.
Definition: ApiResult.php:35
__construct(ApiResult $result, Language $lang, $format, $useDB=false)
formatException( $exception, array $options=[])
Format an exception as an array.
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:584
getDummyTitle()
Fetch a dummy title to set on Messages.
static newFromSpecifier( $value)
Transform a MessageSpecifier or a primitive value used interchangeably with specifiers (a message key...
Definition: Message.php:430
Variant of the Message class.
Definition: RawMessage.php:34
getFormat()
Fetch the format for this formatter.
getParams()
Returns the message parameters.
getMessageFromException( $exception, array $options=[])
Get an ApiMessage from an exception.
getErrors()
Get the list of errors.
const META_CONTENT
Key for the &#39;content&#39; metadata item.
Definition: ApiResult.php:90
getResultData( $path=[], $transforms=[])
Get the result data array.
Definition: ApiResult.php:254
arrayFromStatus(StatusValue $status, $type='error', $format=null)
Format messages from a StatusValue as an array.
newWithFormat( $format)
Return a formatter like this one but with a different format.
getLanguage()
Fetch the Language for this formatter.
return true
Definition: router.php:92
Interface for MediaWiki-localized exceptions.
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:40
static Title $dummyTitle
Dummy title to silence warnings from MessageCache::parse()