MediaWiki  master
ResponseFactory.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest;
4 
5 use HttpStatus;
6 use InvalidArgumentException;
7 use LanguageCode;
9 use stdClass;
10 use Throwable;
13 
18  private const CT_PLAIN = 'text/plain; charset=utf-8';
19  private const CT_HTML = 'text/html; charset=utf-8';
20  private const CT_JSON = 'application/json';
21 
23  private $textFormatters;
24 
28  public function __construct( $textFormatters ) {
29  $this->textFormatters = $textFormatters;
30  }
31 
39  public function encodeJson( $value ) {
40  $json = json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
41  if ( $json === false ) {
42  throw new JsonEncodingException( json_last_error_msg(), json_last_error() );
43  }
44  return $json;
45  }
46 
52  public function create() {
53  return new Response();
54  }
55 
63  public function createJson( $value, $contentType = null ) {
64  $contentType = $contentType ?? self::CT_JSON;
65  $response = new Response( $this->encodeJson( $value ) );
66  $response->setHeader( 'Content-Type', $contentType );
67  return $response;
68  }
69 
80  public function createNoContent() {
81  $response = new Response();
82  $response->setStatus( 204 );
83  return $response;
84  }
85 
95  public function createPermanentRedirect( $target ) {
96  $response = $this->createRedirectBase( $target );
97  $response->setStatus( 301 );
98  return $response;
99  }
100 
111  public function createLegacyTemporaryRedirect( $target ) {
112  $response = $this->createRedirectBase( $target );
113  $response->setStatus( 302 );
114  return $response;
115  }
116 
125  public function createTemporaryRedirect( $target ) {
126  $response = $this->createRedirectBase( $target );
127  $response->setStatus( 307 );
128  return $response;
129  }
130 
139  public function createSeeOther( $target ) {
140  $response = $this->createRedirectBase( $target );
141  $response->setStatus( 303 );
142  return $response;
143  }
144 
155  public function createNotModified() {
156  $response = new Response();
157  $response->setStatus( 304 );
158  return $response;
159  }
160 
168  public function createHttpError( $errorCode, array $bodyData = [] ) {
169  if ( $errorCode < 400 || $errorCode >= 600 ) {
170  throw new InvalidArgumentException( 'error code must be 4xx or 5xx' );
171  }
172  $response = $this->createJson( $bodyData + [
173  'httpCode' => $errorCode,
174  'httpReason' => HttpStatus::getMessage( $errorCode )
175  ] );
176  // TODO add link to error code documentation
177  $response->setStatus( $errorCode );
178  return $response;
179  }
180 
190  public function createLocalizedHttpError(
191  $errorCode,
192  MessageValue $messageValue,
193  array $extraData = []
194  ) {
195  return $this->createHttpError(
196  $errorCode,
197  array_merge( $extraData, $this->formatMessage( $messageValue ) )
198  );
199  }
200 
206  public function createFromException( Throwable $exception ) {
207  if ( $exception instanceof LocalizedHttpException ) {
208  $response = $this->createLocalizedHttpError(
209  $exception->getCode(),
210  $exception->getMessageValue(),
211  (array)$exception->getErrorData()
212  );
213  } elseif ( $exception instanceof ResponseException ) {
214  return $exception->getResponse();
215  } elseif ( $exception instanceof RedirectException ) {
216  $response = $this->createRedirectBase( $exception->getTarget() );
217  $response->setStatus( $exception->getCode() );
218  } elseif ( $exception instanceof HttpException ) {
219  if ( in_array( $exception->getCode(), [ 204, 304 ], true ) ) {
220  $response = $this->create();
221  $response->setStatus( $exception->getCode() );
222  } else {
223  $response = $this->createHttpError(
224  $exception->getCode(),
225  array_merge(
226  [ 'message' => $exception->getMessage() ],
227  (array)$exception->getErrorData()
228  )
229  );
230  }
231  } else {
232  $response = $this->createHttpError( 500, [
233  'message' => 'Error: exception of type ' . get_class( $exception ),
234  'exception' => MWExceptionHandler::getStructuredExceptionData( $exception )
235  ] );
236  // FIXME should we try to do something useful with ILocalizedException?
237  // FIXME should we try to do something useful with common MediaWiki errors like ReadOnlyError?
238  }
239  return $response;
240  }
241 
249  public function createFromReturnValue( $value ) {
250  $originalValue = $value;
251  if ( is_scalar( $value ) ) {
252  $data = [ 'value' => $value ];
253  } elseif ( is_array( $value ) || $value instanceof stdClass ) {
254  $data = $value;
255  } else {
256  $type = gettype( $originalValue );
257  if ( $type === 'object' ) {
258  $type = get_class( $originalValue );
259  }
260  throw new InvalidArgumentException( __METHOD__ . ": Invalid return value type $type" );
261  }
262  $response = $this->createJson( $data );
263  return $response;
264  }
265 
271  protected function createRedirectBase( $target ) {
272  $response = new Response( $this->getHyperLink( $target ) );
273  $response->setHeader( 'Content-Type', self::CT_HTML );
274  $response->setHeader( 'Location', $target );
275  return $response;
276  }
277 
284  protected function getHyperLink( $url ) {
285  $url = htmlspecialchars( $url );
286  return "<!doctype html><title>Redirect</title><a href=\"$url\">$url</a>";
287  }
288 
289  public function formatMessage( MessageValue $messageValue ) {
290  if ( !$this->textFormatters ) {
291  // For unit tests
292  return [];
293  }
294  $translations = [];
295  foreach ( $this->textFormatters as $formatter ) {
296  $lang = LanguageCode::bcp47( $formatter->getLangCode() );
297  $messageText = $formatter->format( $messageValue );
298  $translations[$lang] = $messageText;
299  }
300  return [ 'messageTranslations' => $translations ];
301  }
302 
303 }
MediaWiki\Rest\ResponseFactory\createFromException
createFromException(Throwable $exception)
Turn a throwable into a JSON error response.
Definition: ResponseFactory.php:206
MediaWiki\Rest\ResponseFactory
Generates standardized response objects.
Definition: ResponseFactory.php:17
Wikimedia\Message\ITextFormatter
Definition: ITextFormatter.php:18
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
HttpStatus
Definition: HttpStatus.php:26
MediaWiki\Rest\ResponseFactory\createRedirectBase
createRedirectBase( $target)
Create a redirect response with type / response code unspecified.
Definition: ResponseFactory.php:271
MWExceptionHandler
Handler class for MWExceptions.
Definition: MWExceptionHandler.php:31
MediaWiki\Rest\ResponseFactory\getHyperLink
getHyperLink( $url)
Returns a minimal HTML document that links to the given URL, as suggested by RFC 7231 for 3xx respons...
Definition: ResponseFactory.php:284
MediaWiki\Rest\ResponseFactory\createNotModified
createNotModified()
Create a 304 (Not Modified) response, used when the client has an up-to-date cached response.
Definition: ResponseFactory.php:155
MediaWiki\Rest\ResponseFactory\createLegacyTemporaryRedirect
createLegacyTemporaryRedirect( $target)
Creates a temporary (302) redirect.
Definition: ResponseFactory.php:111
MediaWiki\Rest\ResponseFactory\createJson
createJson( $value, $contentType=null)
Create a successful JSON response.
Definition: ResponseFactory.php:63
MediaWiki\Rest\RedirectException
This is an exception class that extends HttpException and will generate a redirect when handled.
Definition: RedirectException.php:15
MediaWiki\Rest\ResponseFactory\formatMessage
formatMessage(MessageValue $messageValue)
Definition: ResponseFactory.php:289
MWExceptionHandler\getStructuredExceptionData
static getStructuredExceptionData(Throwable $e, $catcher=self::CAUGHT_BY_OTHER)
Get a structured representation of a Throwable.
Definition: MWExceptionHandler.php:554
MediaWiki\Rest\ResponseFactory\createFromReturnValue
createFromReturnValue( $value)
Create a JSON response from an arbitrary value.
Definition: ResponseFactory.php:249
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:16
MediaWiki\Rest\ResponseFactory\createNoContent
createNoContent()
Create a 204 (No Content) response, used to indicate that an operation which does not return anything...
Definition: ResponseFactory.php:80
MediaWiki\Rest\ResponseFactory\createTemporaryRedirect
createTemporaryRedirect( $target)
Creates a temporary (307) redirect.
Definition: ResponseFactory.php:125
MediaWiki\Rest\Response
Definition: Response.php:8
MediaWiki\Rest
MediaWiki\Rest\ResponseFactory\createHttpError
createHttpError( $errorCode, array $bodyData=[])
Create a HTTP 4xx or 5xx response.
Definition: ResponseFactory.php:168
MediaWiki\Rest\ResponseFactory\CT_HTML
const CT_HTML
Definition: ResponseFactory.php:19
MediaWiki\Rest\HttpException
This is the base exception class for non-fatal exceptions thrown from REST handlers.
Definition: HttpException.php:12
MediaWiki\Rest\ResponseException
This is an exception class that wraps a Response and extends HttpException.
Definition: ResponseException.php:17
MediaWiki\Rest\ResponseFactory\createPermanentRedirect
createPermanentRedirect( $target)
Creates a permanent (301) redirect.
Definition: ResponseFactory.php:95
HttpStatus\getMessage
static getMessage( $code)
Get the message associated with an HTTP response status code.
Definition: HttpStatus.php:34
MediaWiki\Rest\ResponseFactory\CT_JSON
const CT_JSON
Definition: ResponseFactory.php:20
MediaWiki\Rest\ResponseFactory\__construct
__construct( $textFormatters)
Definition: ResponseFactory.php:28
MediaWiki\Rest\ResponseFactory\createSeeOther
createSeeOther( $target)
Creates a See Other (303) redirect.
Definition: ResponseFactory.php:139
MediaWiki\Rest\ResponseFactory\create
create()
Create an unspecified response.
Definition: ResponseFactory.php:52
LanguageCode\bcp47
static bcp47( $code)
Get the normalised IETF language tag See unit test for examples.
Definition: LanguageCode.php:175
MediaWiki\Rest\ResponseFactory\createLocalizedHttpError
createLocalizedHttpError( $errorCode, MessageValue $messageValue, array $extraData=[])
Create an HTTP 4xx or 5xx response with error message localisation.
Definition: ResponseFactory.php:190
LanguageCode
Methods for dealing with language codes.
Definition: LanguageCode.php:27
MediaWiki\Rest\JsonEncodingException
Definition: JsonEncodingException.php:5
MediaWiki\Rest\ResponseFactory\CT_PLAIN
const CT_PLAIN
Definition: ResponseFactory.php:18
MediaWiki\Rest\ResponseFactory\encodeJson
encodeJson( $value)
Encode a stdClass object or array to a JSON string.
Definition: ResponseFactory.php:39
MediaWiki\Rest\ResponseFactory\$textFormatters
ITextFormatter[] $textFormatters
Definition: ResponseFactory.php:23
MediaWiki\Rest\LocalizedHttpException
@newable
Definition: LocalizedHttpException.php:10
$type
$type
Definition: testCompression.php:52