MediaWiki REL1_39
ApiFormatBase.php
Go to the documentation of this file.
1<?php
26
32abstract class ApiFormatBase extends ApiBase {
33 private $mIsHtml, $mFormat;
34 private $mBuffer, $mDisabled = false;
35 private $mIsWrappedHtml = false;
36 private $mHttpStatus = false;
37 protected $mForceDefaultParams = false;
38
44 public function __construct( ApiMain $main, $format ) {
45 parent::__construct( $main, $format );
46
47 $this->mIsHtml = ( substr( $format, -2, 2 ) === 'fm' ); // ends with 'fm'
48 if ( $this->mIsHtml ) {
49 $this->mFormat = substr( $format, 0, -2 ); // remove ending 'fm'
50 $this->mIsWrappedHtml = $this->getMain()->getCheck( 'wrappedhtml' );
51 } else {
52 $this->mFormat = $format;
53 }
54 $this->mFormat = strtoupper( $this->mFormat );
55 }
56
65 abstract public function getMimeType();
66
74 public function getFilename() {
75 if ( $this->getIsWrappedHtml() ) {
76 return 'api-result-wrapped.json';
77 } elseif ( $this->getIsHtml() ) {
78 return 'api-result.html';
79 } else {
80 $mimeAnalyzer = MediaWikiServices::getInstance()->getMimeAnalyzer();
81 $ext = $mimeAnalyzer->getExtensionFromMimeTypeOrNull( $this->getMimeType() )
82 ?? strtolower( $this->mFormat );
83 return "api-result.$ext";
84 }
85 }
86
91 public function getFormat() {
92 return $this->mFormat;
93 }
94
101 public function getIsHtml() {
102 return $this->mIsHtml;
103 }
104
110 protected function getIsWrappedHtml() {
111 return $this->mIsWrappedHtml;
112 }
113
119 public function disable() {
120 $this->mDisabled = true;
121 }
122
127 public function isDisabled() {
128 return $this->mDisabled;
129 }
130
139 public function canPrintErrors() {
140 return true;
141 }
142
149 public function forceDefaultParams() {
150 $this->mForceDefaultParams = true;
151 }
152
158 protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
159 if ( !$this->mForceDefaultParams ) {
160 return parent::getParameterFromSettings( $paramName, $paramSettings, $parseLimit );
161 }
162
163 if ( !is_array( $paramSettings ) ) {
164 return $paramSettings;
165 }
166
167 return $paramSettings[ParamValidator::PARAM_DEFAULT] ?? null;
168 }
169
175 public function setHttpStatus( $code ) {
176 if ( $this->mDisabled ) {
177 return;
178 }
179
180 if ( $this->getIsHtml() ) {
181 $this->mHttpStatus = $code;
182 } else {
183 $this->getMain()->getRequest()->response()->statusHeader( $code );
184 }
185 }
186
191 public function initPrinter( $unused = false ) {
192 if ( $this->mDisabled ) {
193 return;
194 }
195
196 if ( $this->getIsHtml() && $this->getMain()->getCacheMode() === 'public' ) {
197 // The HTML may contain user secrets! T354045
198 $this->getMain()->setCacheMode( 'anon-public-user-private' );
199 }
200
201 $mime = $this->getIsWrappedHtml()
202 ? 'text/mediawiki-api-prettyprint-wrapped'
203 : ( $this->getIsHtml() ? 'text/html' : $this->getMimeType() );
204
205 // Some printers (ex. Feed) do their own header settings,
206 // in which case $mime will be set to null
207 if ( $mime === null ) {
208 return; // skip any initialization
209 }
210
211 $this->getMain()->getRequest()->response()->header( "Content-Type: $mime; charset=utf-8" );
212
213 // Set X-Frame-Options API results (T41180)
214 $apiFrameOptions = $this->getConfig()->get( MainConfigNames::ApiFrameOptions );
215 if ( $apiFrameOptions ) {
216 $this->getMain()->getRequest()->response()->header( "X-Frame-Options: $apiFrameOptions" );
217 }
218
219 // Set a Content-Disposition header so something downloading an API
220 // response uses a halfway-sensible filename (T128209).
221 $header = 'Content-Disposition: inline';
222 $filename = $this->getFilename();
223 $compatFilename = mb_convert_encoding( $filename, 'ISO-8859-1' );
224 if ( preg_match( '/^[0-9a-zA-Z!#$%&\'*+\-.^_`|~]+$/', $compatFilename ) ) {
225 $header .= '; filename=' . $compatFilename;
226 } else {
227 $header .= '; filename="'
228 . preg_replace( '/([\0-\x1f"\x5c\x7f])/', '\\\\$1', $compatFilename ) . '"';
229 }
230 if ( $compatFilename !== $filename ) {
231 $value = "UTF-8''" . rawurlencode( $filename );
232 // rawurlencode() encodes more characters than RFC 5987 specifies. Unescape the ones it allows.
233 $value = strtr( $value, [
234 '%21' => '!', '%23' => '#', '%24' => '$', '%26' => '&', '%2B' => '+', '%5E' => '^',
235 '%60' => '`', '%7C' => '|',
236 ] );
237 $header .= '; filename*=' . $value;
238 }
239 $this->getMain()->getRequest()->response()->header( $header );
240 }
241
245 public function closePrinter() {
246 if ( $this->mDisabled ) {
247 return;
248 }
249
250 $mime = $this->getMimeType();
251 if ( $this->getIsHtml() && $mime !== null ) {
252 $format = $this->getFormat();
253 $lcformat = strtolower( $format );
254 $result = $this->getBuffer();
255
256 $context = new DerivativeContext( $this->getMain() );
257 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
258 $context->setSkin( $skinFactory->makeSkin( 'apioutput' ) );
259 $context->setTitle( SpecialPage::getTitleFor( 'ApiHelp' ) );
260 $out = new OutputPage( $context );
261 $context->setOutput( $out );
262
263 $out->setRobotPolicy( 'noindex,nofollow' );
264 $out->addModuleStyles( 'mediawiki.apipretty' );
265 $out->setPageTitle( $context->msg( 'api-format-title' ) );
266
267 if ( !$this->getIsWrappedHtml() ) {
268 // When the format without suffix 'fm' is defined, there is a non-html version
269 if ( $this->getMain()->getModuleManager()->isDefined( $lcformat, 'format' ) ) {
270 if ( !$this->getRequest()->wasPosted() ) {
271 $nonHtmlUrl = strtok( $this->getRequest()->getFullRequestURL(), '?' )
272 . '?' . $this->getRequest()->appendQueryValue( 'format', $lcformat );
273 $msg = $context->msg( 'api-format-prettyprint-header-hyperlinked' )
274 ->params( $format, $lcformat, $nonHtmlUrl );
275 } else {
276 $msg = $context->msg( 'api-format-prettyprint-header' )->params( $format, $lcformat );
277 }
278 } else {
279 $msg = $context->msg( 'api-format-prettyprint-header-only-html' )->params( $format );
280 }
281
282 $header = $msg->parseAsBlock();
283 $out->addHTML(
284 Html::rawElement( 'div', [ 'class' => 'api-pretty-header' ],
286 )
287 );
288
289 if ( $this->mHttpStatus && $this->mHttpStatus !== 200 ) {
290 $out->addHTML(
291 Html::rawElement( 'div', [ 'class' => [ 'api-pretty-header', 'api-pretty-status' ] ],
292 $this->msg(
293 'api-format-prettyprint-status',
294 $this->mHttpStatus,
295 HttpStatus::getMessage( $this->mHttpStatus )
296 )->parse()
297 )
298 );
299 }
300 }
301
302 if ( $this->getHookRunner()->onApiFormatHighlight( $context, $result, $mime, $format ) ) {
303 $out->addHTML(
304 Html::element( 'pre', [ 'class' => 'api-pretty-content' ], $result )
305 );
306 }
307
308 if ( $this->getIsWrappedHtml() ) {
309 // This is a special output mode mainly intended for ApiSandbox use
310 $time = $this->getMain()->getRequest()->getElapsedTime();
311 echo FormatJson::encode(
312 [
313 'status' => (int)( $this->mHttpStatus ?: 200 ),
314 'statustext' => HttpStatus::getMessage( $this->mHttpStatus ?: 200 ),
315 'html' => $out->getHTML(),
316 'modules' => array_values( array_unique( array_merge(
317 $out->getModules(),
318 $out->getModuleStyles()
319 ) ) ),
320 'continue' => $this->getResult()->getResultData( 'continue' ),
321 'time' => round( $time * 1000 ),
322 ],
323 false, FormatJson::ALL_OK
324 );
325 } else {
326 // API handles its own clickjacking protection.
327 // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode.
328 $out->setPreventClickjacking( false );
329 $out->output();
330 }
331 } else {
332 // For non-HTML output, clear all errors that might have been
333 // displayed if display_errors=On
334 ob_clean();
335
336 echo $this->getBuffer();
337 }
338 }
339
344 public function printText( $text ) {
345 $this->mBuffer .= $text;
346 }
347
352 public function getBuffer() {
353 return $this->mBuffer;
354 }
355
356 public function getAllowedParams() {
357 $ret = [];
358 if ( $this->getIsHtml() ) {
359 $ret['wrappedhtml'] = [
360 ParamValidator::PARAM_DEFAULT => false,
361 ApiBase::PARAM_HELP_MSG => 'apihelp-format-param-wrappedhtml',
362 ];
363 }
364 return $ret;
365 }
366
367 protected function getExamplesMessages() {
368 return [
369 'action=query&meta=siteinfo&siprop=namespaces&format=' . $this->getModuleName()
370 => [ 'apihelp-format-example-generic', $this->getFormat() ]
371 ];
372 }
373
374 public function getHelpUrls() {
375 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Data_formats';
376 }
377
378}
379
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:56
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition ApiBase.php:307
getMain()
Get the main module.
Definition ApiBase.php:514
getResult()
Get the result object.
Definition ApiBase.php:629
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:163
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:498
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:711
This is the abstract base class for API formatters.
getHelpUrls()
Return links to more detailed help pages about the module.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
__construct(ApiMain $main, $format)
If $format ends with 'fm', pretty-print the output in HTML.
getMimeType()
Overriding class returns the MIME type that should be sent to the client.
getFormat()
Get the internal format name.
getFilename()
Return a filename for this module's output.
printText( $text)
Append text to the output buffer.
initPrinter( $unused=false)
Initialize the printer function and prepare the output headers.
disable()
Disable the formatter.
getBuffer()
Get the contents of the buffer.
getIsWrappedHtml()
Returns true when the special wrapped mode is enabled.
canPrintErrors()
Whether this formatter can handle printing API errors.
getExamplesMessages()
Returns usage examples for this module.
isDisabled()
Whether the printer is disabled.
forceDefaultParams()
Ignore request parameters, force a default.
getIsHtml()
Returns true when the HTML pretty-printer should be used.
getParameterFromSettings( $paramName, $paramSettings, $parseLimit)
Overridden to honor $this->forceDefaultParams(), if applicable Using the settings determine the value...
setHttpStatus( $code)
Set the HTTP status code to be used for the response.
closePrinter()
Finish printing and output buffered data.
static fixHelpLinks( $html, $helptitle=null, $localModules=[])
Replace Special:ApiHelp links with links to api.php.
Definition ApiHelp.php:208
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:52
msg( $key,... $params)
Get a Message object with context set Parameters are the same as wfMessage()
An IContextSource implementation which will inherit context from another source but allow individual ...
A class containing constants representing the names of configuration variables.
Service locator for MediaWiki core services.
This is one of the Core classes and should be read at least once by any new developers.
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
Service for formatting and validating API parameters.
$mime
Definition router.php:60
if(!is_readable( $file)) $ext
Definition router.php:48
$header