MediaWiki REL1_40
ApiFormatBase.php
Go to the documentation of this file.
1<?php
27
33abstract class ApiFormatBase extends ApiBase {
34 private $mIsHtml, $mFormat;
35 private $mBuffer, $mDisabled = false;
36 private $mIsWrappedHtml = false;
37 private $mHttpStatus = false;
38 protected $mForceDefaultParams = false;
39
45 public function __construct( ApiMain $main, $format ) {
46 parent::__construct( $main, $format );
47
48 $this->mIsHtml = str_ends_with( $format, 'fm' );
49 if ( $this->mIsHtml ) {
50 $this->mFormat = substr( $format, 0, -2 ); // remove ending 'fm'
51 $this->mIsWrappedHtml = $this->getMain()->getCheck( 'wrappedhtml' );
52 } else {
53 $this->mFormat = $format;
54 }
55 $this->mFormat = strtoupper( $this->mFormat );
56 }
57
66 abstract public function getMimeType();
67
75 public function getFilename() {
76 if ( $this->getIsWrappedHtml() ) {
77 return 'api-result-wrapped.json';
78 } elseif ( $this->getIsHtml() ) {
79 return 'api-result.html';
80 } else {
81 $mimeAnalyzer = MediaWikiServices::getInstance()->getMimeAnalyzer();
82 $ext = $mimeAnalyzer->getExtensionFromMimeTypeOrNull( $this->getMimeType() )
83 ?? strtolower( $this->mFormat );
84 return "api-result.$ext";
85 }
86 }
87
92 public function getFormat() {
93 return $this->mFormat;
94 }
95
102 public function getIsHtml() {
103 return $this->mIsHtml;
104 }
105
111 protected function getIsWrappedHtml() {
112 return $this->mIsWrappedHtml;
113 }
114
120 public function disable() {
121 $this->mDisabled = true;
122 }
123
128 public function isDisabled() {
129 return $this->mDisabled;
130 }
131
140 public function canPrintErrors() {
141 return true;
142 }
143
150 public function forceDefaultParams() {
151 $this->mForceDefaultParams = true;
152 }
153
159 protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) {
160 if ( !$this->mForceDefaultParams ) {
161 return parent::getParameterFromSettings( $paramName, $paramSettings, $parseLimit );
162 }
163
164 if ( !is_array( $paramSettings ) ) {
165 return $paramSettings;
166 }
167
168 return $paramSettings[ParamValidator::PARAM_DEFAULT] ?? null;
169 }
170
176 public function setHttpStatus( $code ) {
177 if ( $this->mDisabled ) {
178 return;
179 }
180
181 if ( $this->getIsHtml() ) {
182 $this->mHttpStatus = $code;
183 } else {
184 $this->getMain()->getRequest()->response()->statusHeader( $code );
185 }
186 }
187
192 public function initPrinter( $unused = false ) {
193 if ( $this->mDisabled ) {
194 return;
195 }
196
197 if ( $this->getIsHtml() && $this->getMain()->getCacheMode() === 'public' ) {
198 // The HTML may contain user secrets! T354045
199 $this->getMain()->setCacheMode( 'anon-public-user-private' );
200 }
201
202 $mime = $this->getIsWrappedHtml()
203 ? 'text/mediawiki-api-prettyprint-wrapped'
204 : ( $this->getIsHtml() ? 'text/html' : $this->getMimeType() );
205
206 // Some printers (ex. Feed) do their own header settings,
207 // in which case $mime will be set to null
208 if ( $mime === null ) {
209 return; // skip any initialization
210 }
211
212 $this->getMain()->getRequest()->response()->header( "Content-Type: $mime; charset=utf-8" );
213
214 // Set X-Frame-Options API results (T41180)
215 $apiFrameOptions = $this->getConfig()->get( MainConfigNames::ApiFrameOptions );
216 if ( $apiFrameOptions ) {
217 $this->getMain()->getRequest()->response()->header( "X-Frame-Options: $apiFrameOptions" );
218 }
219
220 // Set a Content-Disposition header so something downloading an API
221 // response uses a halfway-sensible filename (T128209).
222 $header = 'Content-Disposition: inline';
223 $filename = $this->getFilename();
224 $compatFilename = mb_convert_encoding( $filename, 'ISO-8859-1' );
225 if ( preg_match( '/^[0-9a-zA-Z!#$%&\'*+\-.^_`|~]+$/', $compatFilename ) ) {
226 $header .= '; filename=' . $compatFilename;
227 } else {
228 $header .= '; filename="'
229 . preg_replace( '/([\0-\x1f"\x5c\x7f])/', '\\\\$1', $compatFilename ) . '"';
230 }
231 if ( $compatFilename !== $filename ) {
232 $value = "UTF-8''" . rawurlencode( $filename );
233 // rawurlencode() encodes more characters than RFC 5987 specifies. Unescape the ones it allows.
234 $value = strtr( $value, [
235 '%21' => '!', '%23' => '#', '%24' => '$', '%26' => '&', '%2B' => '+', '%5E' => '^',
236 '%60' => '`', '%7C' => '|',
237 ] );
238 $header .= '; filename*=' . $value;
239 }
240 $this->getMain()->getRequest()->response()->header( $header );
241 }
242
246 public function closePrinter() {
247 if ( $this->mDisabled ) {
248 return;
249 }
250
251 $mime = $this->getMimeType();
252 if ( $this->getIsHtml() && $mime !== null ) {
253 $format = $this->getFormat();
254 $lcformat = strtolower( $format );
255 $result = $this->getBuffer();
256
257 $context = new DerivativeContext( $this->getMain() );
258 $skinFactory = MediaWikiServices::getInstance()->getSkinFactory();
259 $context->setSkin( $skinFactory->makeSkin( 'apioutput' ) );
260 $context->setTitle( SpecialPage::getTitleFor( 'ApiHelp' ) );
261 $out = new OutputPage( $context );
262 $context->setOutput( $out );
263
264 $out->setRobotPolicy( 'noindex,nofollow' );
265 $out->addModuleStyles( 'mediawiki.apipretty' );
266 $out->setPageTitle( $context->msg( 'api-format-title' ) );
267
268 if ( !$this->getIsWrappedHtml() ) {
269 // When the format without suffix 'fm' is defined, there is a non-html version
270 if ( $this->getMain()->getModuleManager()->isDefined( $lcformat, 'format' ) ) {
271 if ( !$this->getRequest()->wasPosted() ) {
272 $nonHtmlUrl = strtok( $this->getRequest()->getFullRequestURL(), '?' )
273 . '?' . $this->getRequest()->appendQueryValue( 'format', $lcformat );
274 $msg = $context->msg( 'api-format-prettyprint-header-hyperlinked' )
275 ->params( $format, $lcformat, $nonHtmlUrl );
276 } else {
277 $msg = $context->msg( 'api-format-prettyprint-header' )->params( $format, $lcformat );
278 }
279 } else {
280 $msg = $context->msg( 'api-format-prettyprint-header-only-html' )->params( $format );
281 }
282
283 $header = $msg->parseAsBlock();
284 $out->addHTML(
285 Html::rawElement( 'div', [ 'class' => 'api-pretty-header' ],
287 )
288 );
289
290 if ( $this->mHttpStatus && $this->mHttpStatus !== 200 ) {
291 $out->addHTML(
292 Html::rawElement( 'div', [ 'class' => [ 'api-pretty-header', 'api-pretty-status' ] ],
293 $this->msg(
294 'api-format-prettyprint-status',
295 $this->mHttpStatus,
296 HttpStatus::getMessage( $this->mHttpStatus )
297 )->parse()
298 )
299 );
300 }
301 }
302
303 if ( $this->getHookRunner()->onApiFormatHighlight( $context, $result, $mime, $format ) ) {
304 $out->addHTML(
305 Html::element( 'pre', [ 'class' => 'api-pretty-content' ], $result )
306 );
307 }
308
309 if ( $this->getIsWrappedHtml() ) {
310 // This is a special output mode mainly intended for ApiSandbox use
311 $time = $this->getMain()->getRequest()->getElapsedTime();
312 echo FormatJson::encode(
313 [
314 'status' => (int)( $this->mHttpStatus ?: 200 ),
315 'statustext' => HttpStatus::getMessage( $this->mHttpStatus ?: 200 ),
316 'html' => $out->getHTML(),
317 'modules' => array_values( array_unique( array_merge(
318 $out->getModules(),
319 $out->getModuleStyles()
320 ) ) ),
321 'continue' => $this->getResult()->getResultData( 'continue' ),
322 'time' => round( $time * 1000 ),
323 ],
324 false, FormatJson::ALL_OK
325 );
326 } else {
327 // API handles its own clickjacking protection.
328 // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode.
329 $out->setPreventClickjacking( false );
330 $out->output();
331 }
332 } else {
333 // For non-HTML output, clear all errors that might have been
334 // displayed if display_errors=On
335 ob_clean();
336
337 echo $this->getBuffer();
338 }
339 }
340
345 public function printText( $text ) {
346 $this->mBuffer .= $text;
347 }
348
353 public function getBuffer() {
354 return $this->mBuffer;
355 }
356
357 public function getAllowedParams() {
358 $ret = [];
359 if ( $this->getIsHtml() ) {
360 $ret['wrappedhtml'] = [
361 ParamValidator::PARAM_DEFAULT => false,
362 ApiBase::PARAM_HELP_MSG => 'apihelp-format-param-wrappedhtml',
363 ];
364 }
365 return $ret;
366 }
367
368 protected function getExamplesMessages() {
369 return [
370 'action=query&meta=siteinfo&siprop=namespaces&format=' . $this->getModuleName()
371 => [ 'apihelp-format-example-generic', $this->getFormat() ]
372 ];
373 }
374
375 public function getHelpUrls() {
376 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Data_formats';
377 }
378
379}
380
This abstract class implements many basic API functions, and is the base of all API classes.
Definition ApiBase.php:59
getModuleManager()
Get the module manager, or null if this module has no sub-modules.
Definition ApiBase.php:315
getMain()
Get the main module.
Definition ApiBase.php:522
getResult()
Get the result object.
Definition ApiBase.php:637
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:166
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:506
getHookRunner()
Get an ApiHookRunner for running core API hooks.
Definition ApiBase.php:719
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:212
This is the main API class, used for both external and internal processing.
Definition ApiMain.php:58
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 ...
This class is a collection of static functions that serve two purposes:
Definition Html.php:55
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