MediaWiki REL1_31
ApiFormatXml.php
Go to the documentation of this file.
1<?php
28
29 private $mRootElemName = 'api';
30 public static $namespace = 'http://www.mediawiki.org/xml/api/';
31 private $mIncludeNamespace = false;
32 private $mXslt = null;
33
34 public function getMimeType() {
35 return 'text/xml';
36 }
37
38 public function setRootElement( $rootElemName ) {
39 $this->mRootElemName = $rootElemName;
40 }
41
42 public function execute() {
44 $this->mIncludeNamespace = $params['includexmlnamespace'];
45 $this->mXslt = $params['xslt'];
46
47 $this->printText( '<?xml version="1.0"?>' );
48 if ( !is_null( $this->mXslt ) ) {
49 $this->addXslt();
50 }
51
52 $result = $this->getResult();
53 if ( $this->mIncludeNamespace && $result->getResultData( 'xmlns' ) === null ) {
54 // If the result data already contains an 'xmlns' namespace added
55 // for custom XML output types, it will override the one for the
56 // generic API results.
57 // This allows API output of other XML types like Atom, RSS, RSD.
58 $result->addValue( null, 'xmlns', self::$namespace, ApiResult::NO_SIZE_CHECK );
59 }
60 $data = $result->getResultData( null, [
61 'Custom' => function ( &$data, &$metadata ) {
62 if ( isset( $metadata[ApiResult::META_TYPE] ) ) {
63 // We want to use non-BC for BCassoc to force outputting of _idx.
64 switch ( $metadata[ApiResult::META_TYPE] ) {
65 case 'BCassoc':
66 $metadata[ApiResult::META_TYPE] = 'assoc';
67 break;
68 }
69 }
70 },
71 'BC' => [ 'nobool', 'no*', 'nosub' ],
72 'Types' => [ 'ArmorKVP' => '_name' ],
73 ] );
74
75 $this->printText(
76 static::recXmlPrint( $this->mRootElemName,
77 $data,
78 $this->getIsHtml() ? -2 : null
79 )
80 );
81 }
82
92 public static function recXmlPrint( $name, $value, $indent, $attributes = [] ) {
93 $retval = '';
94 if ( $indent !== null ) {
95 if ( $name !== null ) {
96 $indent += 2;
97 }
98 $indstr = "\n" . str_repeat( ' ', $indent );
99 } else {
100 $indstr = '';
101 }
102
103 if ( is_object( $value ) ) {
105 }
106 if ( is_array( $value ) ) {
107 $contentKey = isset( $value[ApiResult::META_CONTENT] )
109 : '*';
110 $subelementKeys = isset( $value[ApiResult::META_SUBELEMENTS] )
112 : [];
113 if ( isset( $value[ApiResult::META_BC_SUBELEMENTS] ) ) {
114 $subelementKeys = array_merge(
115 $subelementKeys, $value[ApiResult::META_BC_SUBELEMENTS]
116 );
117 }
118 $preserveKeys = isset( $value[ApiResult::META_PRESERVE_KEYS] )
120 : [];
121 $indexedTagName = isset( $value[ApiResult::META_INDEXED_TAG_NAME] )
123 : '_v';
124 $bcBools = isset( $value[ApiResult::META_BC_BOOLS] )
126 : [];
127 $indexSubelements = isset( $value[ApiResult::META_TYPE] )
128 ? $value[ApiResult::META_TYPE] !== 'array'
129 : false;
130
131 $content = null;
132 $subelements = [];
133 $indexedSubelements = [];
134 foreach ( $value as $k => $v ) {
135 if ( ApiResult::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
136 continue;
137 }
138
139 $oldv = $v;
140 if ( is_bool( $v ) && !in_array( $k, $bcBools, true ) ) {
141 $v = $v ? 'true' : 'false';
142 }
143
144 if ( $name !== null && $k === $contentKey ) {
145 $content = $v;
146 } elseif ( is_int( $k ) ) {
147 $indexedSubelements[$k] = $v;
148 } elseif ( is_array( $v ) || is_object( $v ) ) {
149 $subelements[self::mangleName( $k, $preserveKeys )] = $v;
150 } elseif ( in_array( $k, $subelementKeys, true ) || $name === null ) {
151 $subelements[self::mangleName( $k, $preserveKeys )] = [
152 'content' => $v,
153 ApiResult::META_CONTENT => 'content',
154 ApiResult::META_TYPE => 'assoc',
155 ];
156 } elseif ( is_bool( $oldv ) ) {
157 if ( $oldv ) {
158 $attributes[self::mangleName( $k, $preserveKeys )] = '';
159 }
160 } elseif ( $v !== null ) {
161 $attributes[self::mangleName( $k, $preserveKeys )] = $v;
162 }
163 }
164
165 if ( $content !== null ) {
166 if ( $subelements || $indexedSubelements ) {
167 $subelements[self::mangleName( $contentKey, $preserveKeys )] = [
168 'content' => $content,
169 ApiResult::META_CONTENT => 'content',
170 ApiResult::META_TYPE => 'assoc',
171 ];
172 $content = null;
173 } elseif ( is_scalar( $content ) ) {
174 // Add xml:space="preserve" to the element so XML parsers
175 // will leave whitespace in the content alone
176 $attributes += [ 'xml:space' => 'preserve' ];
177 }
178 }
179
180 if ( $content !== null ) {
181 if ( is_scalar( $content ) ) {
182 $retval .= $indstr . Xml::element( $name, $attributes, $content );
183 } else {
184 if ( $name !== null ) {
185 $retval .= $indstr . Xml::element( $name, $attributes, null );
186 }
187 $retval .= static::recXmlPrint( null, $content, $indent );
188 if ( $name !== null ) {
189 $retval .= $indstr . Xml::closeElement( $name );
190 }
191 }
192 } elseif ( !$indexedSubelements && !$subelements ) {
193 if ( $name !== null ) {
194 $retval .= $indstr . Xml::element( $name, $attributes );
195 }
196 } else {
197 if ( $name !== null ) {
198 $retval .= $indstr . Xml::element( $name, $attributes, null );
199 }
200 foreach ( $subelements as $k => $v ) {
201 $retval .= static::recXmlPrint( $k, $v, $indent );
202 }
203 foreach ( $indexedSubelements as $k => $v ) {
204 $retval .= static::recXmlPrint( $indexedTagName, $v, $indent,
205 $indexSubelements ? [ '_idx' => $k ] : []
206 );
207 }
208 if ( $name !== null ) {
209 $retval .= $indstr . Xml::closeElement( $name );
210 }
211 }
212 } else {
213 // to make sure null value doesn't produce unclosed element,
214 // which is what Xml::element( $name, null, null ) returns
215 if ( $value === null ) {
216 $retval .= $indstr . Xml::element( $name, $attributes );
217 } else {
218 $retval .= $indstr . Xml::element( $name, $attributes, $value );
219 }
220 }
221
222 return $retval;
223 }
224
231 private static function mangleName( $name, $preserveKeys = [] ) {
232 static $nsc = null, $nc = null;
233
234 if ( in_array( $name, $preserveKeys, true ) ) {
235 return $name;
236 }
237
238 if ( $name === '' ) {
239 return '_';
240 }
241
242 if ( $nsc === null ) {
243 // Note we omit ':' from $nsc and $nc because it's reserved for XML
244 // namespacing, and we omit '_' from $nsc (but not $nc) because we
245 // reserve it.
246 $nsc = 'A-Za-z\x{C0}-\x{D6}\x{D8}-\x{F6}\x{F8}-\x{2FF}\x{370}-\x{37D}\x{37F}-\x{1FFF}' .
247 '\x{200C}-\x{200D}\x{2070}-\x{218F}\x{2C00}-\x{2FEF}\x{3001}-\x{D7FF}' .
248 '\x{F900}-\x{FDCF}\x{FDF0}-\x{FFFD}\x{10000}-\x{EFFFF}';
249 $nc = $nsc . '_\-.0-9\x{B7}\x{300}-\x{36F}\x{203F}-\x{2040}';
250 }
251
252 if ( preg_match( "/^[$nsc][$nc]*$/uS", $name ) ) {
253 return $name;
254 }
255
256 return '_' . preg_replace_callback(
257 "/[^$nc]/uS",
258 function ( $m ) {
259 return sprintf( '.%X.', UtfNormal\Utils::utf8ToCodepoint( $m[0] ) );
260 },
261 str_replace( '.', '.2E.', $name )
262 );
263 }
264
265 protected function addXslt() {
266 $nt = Title::newFromText( $this->mXslt );
267 if ( is_null( $nt ) || !$nt->exists() ) {
268 $this->addWarning( 'apiwarn-invalidxmlstylesheet' );
269
270 return;
271 }
272 if ( $nt->getNamespace() != NS_MEDIAWIKI ) {
273 $this->addWarning( 'apiwarn-invalidxmlstylesheetns' );
274
275 return;
276 }
277 if ( substr( $nt->getText(), -4 ) !== '.xsl' ) {
278 $this->addWarning( 'apiwarn-invalidxmlstylesheetext' );
279
280 return;
281 }
282 $this->printText( '<?xml-stylesheet href="' .
283 htmlspecialchars( $nt->getLocalURL( 'action=raw' ) ) . '" type="text/xsl" ?>' );
284 }
285
286 public function getAllowedParams() {
287 return parent::getAllowedParams() + [
288 'xslt' => [
289 ApiBase::PARAM_HELP_MSG => 'apihelp-xml-param-xslt',
290 ],
291 'includexmlnamespace' => [
292 ApiBase::PARAM_DFLT => false,
293 ApiBase::PARAM_HELP_MSG => 'apihelp-xml-param-includexmlnamespace',
294 ],
295 ];
296 }
297}
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition ApiBase.php:48
extractRequestParams( $parseLimit=true)
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:749
getResult()
Get the result object.
Definition ApiBase.php:641
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:124
addWarning( $msg, $code=null, $data=null)
Add a warning for this module.
Definition ApiBase.php:1819
This is the abstract base class for API formatters.
printText( $text)
Append text to the output buffer.
getIsHtml()
Returns true when the HTML pretty-printer should be used.
API XML output formatter.
static $namespace
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
static recXmlPrint( $name, $value, $indent, $attributes=[])
This method takes an array and converts it to XML.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
getMimeType()
Overriding class returns the MIME type that should be sent to the client.
static mangleName( $name, $preserveKeys=[])
Mangle XML-invalid names to be valid in XML.
setRootElement( $rootElemName)
const META_TYPE
Key for the 'type' metadata item.
const META_SUBELEMENTS
Key for the 'subelements' metadata item.
Definition ApiResult.php:76
const META_BC_BOOLS
Key for the 'BC bools' metadata item.
const META_PRESERVE_KEYS
Key for the 'preserve keys' metadata item.
Definition ApiResult.php:82
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:56
const META_CONTENT
Key for the 'content' metadata item.
Definition ApiResult.php:88
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
const META_INDEXED_TAG_NAME
Key for the 'indexed tag name' metadata item.
Definition ApiResult.php:70
static isMetadataKey( $key)
Test whether a key should be considered metadata.
Unicode normalization routines for working with UTF-8 strings.
Definition UtfNormal.php:48
the array() calling protocol came about after MediaWiki 1.4rc1.
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a local account incomplete not yet checked for validity & $retval
Definition hooks.txt:266
Allows to change the fields on the form that will be generated $name
Definition hooks.txt:302
$params