Change API result data structure to be cleaner in new formats
[lhc/web/wiklou.git] / includes / api / ApiResult.php
1 <?php
2 /**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21 /**
22 * This class represents the result of the API operations.
23 * It simply wraps a nested array() structure, adding some functions to simplify
24 * array's modifications. As various modules execute, they add different pieces
25 * of information to this result, structuring it as it will be given to the client.
26 *
27 * Each subarray may either be a dictionary - key-value pairs with unique keys,
28 * or lists, where the items are added using $data[] = $value notation.
29 *
30 * @since 1.25 this is no longer a subclass of ApiBase
31 * @ingroup API
32 */
33 class ApiResult implements ApiSerializable {
34
35 /**
36 * Override existing value in addValue(), setValue(), and similar functions
37 * @since 1.21
38 */
39 const OVERRIDE = 1;
40
41 /**
42 * For addValue(), setValue() and similar functions, if the value does not
43 * exist, add it as the first element. In case the new value has no name
44 * (numerical index), all indexes will be renumbered.
45 * @since 1.21
46 */
47 const ADD_ON_TOP = 2;
48
49 /**
50 * For addValue() and similar functions, do not check size while adding a value
51 * Don't use this unless you REALLY know what you're doing.
52 * Values added while the size checking was disabled will never be counted.
53 * Ignored for setValue() and similar functions.
54 * @since 1.24
55 */
56 const NO_SIZE_CHECK = 4;
57
58 /**
59 * For addValue(), setValue() and similar functions, do not validate data.
60 * Also disables size checking. If you think you need to use this, you're
61 * probably wrong.
62 * @since 1.25
63 */
64 const NO_VALIDATE = 12;
65
66 /**
67 * Key for the 'indexed tag name' metadata item. Value is string.
68 * @since 1.25
69 */
70 const META_INDEXED_TAG_NAME = '_element';
71
72 /**
73 * Key for the 'subelements' metadata item. Value is string[].
74 * @since 1.25
75 */
76 const META_SUBELEMENTS = '_subelements';
77
78 /**
79 * Key for the 'preserve keys' metadata item. Value is string[].
80 * @since 1.25
81 */
82 const META_PRESERVE_KEYS = '_preservekeys';
83
84 /**
85 * Key for the 'content' metadata item. Value is string.
86 * @since 1.25
87 */
88 const META_CONTENT = '_content';
89
90 /**
91 * Key for the 'type' metadata item. Value is one of the following strings:
92 * - default: Like 'array' if all (non-metadata) keys are numeric with no
93 * gaps, otherwise like 'assoc'.
94 * - array: Keys are used for ordering, but are not output. In a format
95 * like JSON, outputs as [].
96 * - assoc: In a format like JSON, outputs as {}.
97 * - kvp: For a format like XML where object keys have a restricted
98 * character set, use an alternative output format. For example,
99 * <container><item name="key">value</item></container> rather than
100 * <container key="value" />
101 * - BCarray: Like 'array' normally, 'default' in backwards-compatibility mode.
102 * - BCassoc: Like 'assoc' normally, 'default' in backwards-compatibility mode.
103 * - BCkvp: Like 'kvp' normally. In backwards-compatibility mode, forces
104 * the alternative output format for all formats, for example
105 * [{"name":key,"*":value}] in JSON. META_KVP_KEY_NAME must also be set.
106 * @since 1.25
107 */
108 const META_TYPE = '_type';
109
110 /**
111 * Key (rather than "name" or other default) for when META_TYPE is 'kvp' or
112 * 'BCkvp'. Value is string.
113 * @since 1.25
114 */
115 const META_KVP_KEY_NAME = '_kvpkeyname';
116
117 /**
118 * Key for the 'BC bools' metadata item. Value is string[].
119 * Note no setter is provided.
120 * @since 1.25
121 */
122 const META_BC_BOOLS = '_BC_bools';
123
124 /**
125 * Key for the 'BC subelements' metadata item. Value is string[].
126 * Note no setter is provided.
127 * @since 1.25
128 */
129 const META_BC_SUBELEMENTS = '_BC_subelements';
130
131 private $data, $size, $maxSize;
132 private $errorFormatter;
133
134 // Deprecated fields
135 private $isRawMode, $checkingSize, $mainForContinuation;
136
137 /**
138 * @param int|bool $maxSize Maximum result "size", or false for no limit
139 * @since 1.25 Takes an integer|bool rather than an ApiMain
140 */
141 public function __construct( $maxSize ) {
142 if ( $maxSize instanceof ApiMain ) {
143 /// @todo: After fixing Wikidata unit tests, warn
144 //wfDeprecated( 'Passing ApiMain to ' . __METHOD__ . ' is deprecated', '1.25' );
145 $this->errorFormatter = $maxSize->getErrorFormatter();
146 $this->mainForContinuation = $maxSize;
147 $maxSize = $maxSize->getConfig()->get( 'APIMaxResultSize' );
148 }
149
150 $this->maxSize = $maxSize;
151 $this->isRawMode = false;
152 $this->checkingSize = true;
153 $this->reset();
154 }
155
156 /**
157 * Set the error formatter
158 * @since 1.25
159 * @param ApiErrorFormatter $formatter
160 */
161 public function setErrorFormatter( ApiErrorFormatter $formatter ) {
162 $this->errorFormatter = $formatter;
163 }
164
165 /**
166 * Allow for adding one ApiResult into another
167 * @since 1.25
168 * @return mixed
169 */
170 public function serializeForApiResult() {
171 return $this->data;
172 }
173
174 /************************************************************************//**
175 * @name Content
176 * @{
177 */
178
179 /**
180 * Clear the current result data.
181 */
182 public function reset() {
183 $this->data = array(
184 self::META_TYPE => 'assoc', // Usually what's desired
185 );
186 $this->size = 0;
187 }
188
189 /**
190 * Get the result data array
191 *
192 * The returned value should be considered read-only.
193 *
194 * Transformations include:
195 *
196 * Custom: (callable) Applied before other transformations. Signature is
197 * function ( &$data, &$metadata ), return value is ignored. Called for
198 * each nested array.
199 *
200 * BC: (array) This transformation does various adjustments to bring the
201 * output in line with the pre-1.25 result format. The value array is a
202 * list of flags: 'nobool', 'no*', 'nosub'.
203 * - Boolean-valued items are changed to '' if true or removed if false,
204 * unless listed in META_BC_BOOLS. This may be skipped by including
205 * 'nobool' in the value array.
206 * - The tag named by META_CONTENT is renamed to '*', and META_CONTENT is
207 * set to '*'. This may be skipped by including 'no*' in the value
208 * array.
209 * - Tags listed in META_BC_SUBELEMENTS will have their values changed to
210 * array( '*' => $value ). This may be skipped by including 'nosub' in
211 * the value array.
212 * - If META_TYPE is 'BCarray', set it to 'default'
213 * - If META_TYPE is 'BCassoc', set it to 'default'
214 * - If META_TYPE is 'BCkvp', perform the transformation (even if
215 * the Types transformation is not being applied).
216 *
217 * Types: (assoc) Apply transformations based on META_TYPE. The values
218 * array is an associative array with the following possible keys:
219 * - AssocAsObject: (bool) If true, return arrays with META_TYPE 'assoc'
220 * as objects.
221 * - ArmorKVP: (string) If provided, transform arrays with META_TYPE 'kvp'
222 * and 'BCkvp' into arrays of two-element arrays, something like this:
223 * $output = array();
224 * foreach ( $input as $key => $value ) {
225 * $pair = array();
226 * $pair[$META_KVP_KEY_NAME ?: $ArmorKVP_value] = $key;
227 * ApiResult::setContentValue( $pair, 'value', $value );
228 * $output[] = $pair;
229 * }
230 *
231 * Strip: (string) Strips metadata keys from the result.
232 * - 'all': Strip all metadata, recursively
233 * - 'base': Strip metadata at the top-level only.
234 * - 'none': Do not strip metadata.
235 * - 'bc': Like 'all', but leave certain pre-1.25 keys.
236 *
237 * @since 1.25
238 * @param array|string|null $path Path to fetch, see ApiResult::addValue
239 * @param array $transforms See above
240 * @return mixed Result data, or null if not found
241 */
242 public function getResultData( $path = array(), $transforms = array() ) {
243 $path = (array)$path;
244 if ( !$path ) {
245 return self::applyTransformations( $this->data, $transforms );
246 }
247
248 $last = array_pop( $path );
249 $ret = &$this->path( $path, 'dummy' );
250 if ( !isset( $ret[$last] ) ) {
251 return null;
252 } elseif ( is_array( $ret[$last] ) ) {
253 return self::applyTransformations( $ret[$last], $transforms );
254 } else {
255 return $ret[$last];
256 }
257 }
258
259 /**
260 * Get the size of the result, i.e. the amount of bytes in it
261 * @return int
262 */
263 public function getSize() {
264 return $this->size;
265 }
266
267 /**
268 * Add an output value to the array by name.
269 *
270 * Verifies that value with the same name has not been added before.
271 *
272 * @since 1.25
273 * @param array &$arr To add $value to
274 * @param string|int|null $name Index of $arr to add $value at,
275 * or null to use the next numeric index.
276 * @param mixed $value
277 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
278 */
279 public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
280 if ( $name === null ) {
281 if ( $flags & ApiResult::ADD_ON_TOP ) {
282 array_unshift( $arr, $value );
283 } else {
284 array_push( $arr, $value );
285 }
286 return;
287 }
288
289 if ( !( $flags & ApiResult::NO_VALIDATE ) ) {
290 $value = self::validateValue( $value );
291 }
292
293 $exists = isset( $arr[$name] );
294 if ( !$exists || ( $flags & ApiResult::OVERRIDE ) ) {
295 if ( !$exists && ( $flags & ApiResult::ADD_ON_TOP ) ) {
296 $arr = array( $name => $value ) + $arr;
297 } else {
298 $arr[$name] = $value;
299 }
300 } elseif ( is_array( $arr[$name] ) && is_array( $value ) ) {
301 $conflicts = array_intersect_key( $arr[$name], $value );
302 if ( !$conflicts ) {
303 $arr[$name] += $value;
304 } else {
305 $keys = join( ', ', array_keys( $conflicts ) );
306 throw new RuntimeException( "Conflicting keys ($keys) when attempting to merge element $name" );
307 }
308 } else {
309 throw new RuntimeException( "Attempting to add element $name=$value, existing value is {$arr[$name]}" );
310 }
311 }
312
313 /**
314 * Validate a value for addition to the result
315 * @param mixed $value
316 */
317 private static function validateValue( $value ) {
318 global $wgContLang;
319
320 if ( is_object( $value ) ) {
321 // Note we use is_callable() here instead of instanceof because
322 // ApiSerializable is an informal protocol (see docs there for details).
323 if ( is_callable( array( $value, 'serializeForApiResult' ) ) ) {
324 $oldValue = $value;
325 $value = $value->serializeForApiResult();
326 if ( is_object( $value ) ) {
327 throw new UnexpectedValueException(
328 get_class( $oldValue ) . "::serializeForApiResult() returned an object of class " .
329 get_class( $value )
330 );
331 }
332
333 // Recursive call instead of fall-through so we can throw a
334 // better exception message.
335 try {
336 return self::validateValue( $value );
337 } catch ( Exception $ex ) {
338 throw new UnexpectedValueException(
339 get_class( $oldValue ) . "::serializeForApiResult() returned an invalid value: " .
340 $ex->getMessage(),
341 0,
342 $ex
343 );
344 }
345 } elseif ( is_callable( array( $value, '__toString' ) ) ) {
346 $value = (string)$value;
347 } else {
348 $value = (array)$value + array( self::META_TYPE => 'assoc' );
349 }
350 }
351 if ( is_array( $value ) ) {
352 foreach ( $value as $k => $v ) {
353 $value[$k] = self::validateValue( $v );
354 }
355 } elseif ( is_float( $value ) && !is_finite( $value ) ) {
356 throw new InvalidArgumentException( "Cannot add non-finite floats to ApiResult" );
357 } elseif ( is_string( $value ) ) {
358 $value = $wgContLang->normalize( $value );
359 } elseif ( $value !== null && !is_scalar( $value ) ) {
360 $type = gettype( $value );
361 if ( is_resource( $value ) ) {
362 $type .= '(' . get_resource_type( $value ) . ')';
363 }
364 throw new InvalidArgumentException( "Cannot add $type to ApiResult" );
365 }
366
367 return $value;
368 }
369
370 /**
371 * Add value to the output data at the given path.
372 *
373 * Path can be an indexed array, each element specifying the branch at which to add the new
374 * value. Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value.
375 * If $path is null, the value will be inserted at the data root.
376 *
377 * @param array|string|int|null $path
378 * @param string|int|null $name See ApiResult::setValue()
379 * @param mixed $value
380 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
381 * This parameter used to be boolean, and the value of OVERRIDE=1 was specifically
382 * chosen so that it would be backwards compatible with the new method signature.
383 * @return bool True if $value fits in the result, false if not
384 * @since 1.21 int $flags replaced boolean $override
385 */
386 public function addValue( $path, $name, $value, $flags = 0 ) {
387 $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
388
389 if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
390 $newsize = $this->size + self::valueSize( $value );
391 if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
392 /// @todo Add i18n message when replacing calls to ->setWarning()
393 $msg = new ApiRawMessage( 'This result was truncated because it would otherwise ' .
394 ' be larger than the limit of $1 bytes', 'truncatedresult' );
395 $msg->numParams( $this->maxSize );
396 $this->errorFormatter->addWarning( 'result', $msg );
397 return false;
398 }
399 $this->size = $newsize;
400 }
401
402 self::setValue( $arr, $name, $value, $flags );
403 return true;
404 }
405
406 /**
407 * Remove an output value to the array by name.
408 * @param array &$arr To remove $value from
409 * @param string|int $name Index of $arr to remove
410 * @return mixed Old value, or null
411 */
412 public static function unsetValue( array &$arr, $name ) {
413 $ret = null;
414 if ( isset( $arr[$name] ) ) {
415 $ret = $arr[$name];
416 unset( $arr[$name] );
417 }
418 return $ret;
419 }
420
421 /**
422 * Remove value from the output data at the given path.
423 *
424 * @since 1.25
425 * @param array|string|null $path See ApiResult::addValue()
426 * @param string|int|null $name Index to remove at $path.
427 * If null, $path itself is removed.
428 * @param int $flags Flags used when adding the value
429 * @return mixed Old value, or null
430 */
431 public function removeValue( $path, $name, $flags = 0 ) {
432 $path = (array)$path;
433 if ( $name === null ) {
434 if ( !$path ) {
435 throw new InvalidArgumentException( 'Cannot remove the data root' );
436 }
437 $name = array_pop( $path );
438 }
439 $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
440 if ( $this->checkingSize && !( $flags & ApiResult::NO_SIZE_CHECK ) ) {
441 $newsize = $this->size - self::valueSize( $ret );
442 $this->size = max( $newsize, 0 );
443 }
444 return $ret;
445 }
446
447 /**
448 * Add an output value to the array by name and mark as META_CONTENT.
449 *
450 * @since 1.25
451 * @param array &$arr To add $value to
452 * @param string|int $name Index of $arr to add $value at.
453 * @param mixed $value
454 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
455 */
456 public static function setContentValue( array &$arr, $name, $value, $flags = 0 ) {
457 if ( $name === null ) {
458 throw new InvalidArgumentException( 'Content value must be named' );
459 }
460 self::setContentField( $arr, $name, $flags );
461 self::setValue( $arr, $name, $value, $flags );
462 }
463
464 /**
465 * Add value to the output data at the given path and mark as META_CONTENT
466 *
467 * @since 1.25
468 * @param array|string|null $path See ApiResult::addValue()
469 * @param string|int $name See ApiResult::setValue()
470 * @param mixed $value
471 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
472 * @return bool True if $value fits in the result, false if not
473 */
474 public function addContentValue( $path, $name, $value, $flags = 0 ) {
475 if ( $name === null ) {
476 throw new InvalidArgumentException( 'Content value must be named' );
477 }
478 $this->addContentField( $path, $name, $flags );
479 $this->addValue( $path, $name, $value, $flags );
480 }
481
482 /**
483 * Add the numeric limit for a limit=max to the result.
484 *
485 * @since 1.25
486 * @param string $moduleName
487 * @param int $limit
488 */
489 public function addParsedLimit( $moduleName, $limit ) {
490 // Add value, allowing overwriting
491 $this->addValue( 'limits', $moduleName, $limit,
492 ApiResult::OVERRIDE | ApiResult::NO_SIZE_CHECK );
493 }
494
495 /**@}*/
496
497 /************************************************************************//**
498 * @name Metadata
499 * @{
500 */
501
502 /**
503 * Set the name of the content field name (META_CONTENT)
504 *
505 * @since 1.25
506 * @param array &$arr
507 * @param string|int $name Name of the field
508 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
509 */
510 public static function setContentField( array &$arr, $name, $flags = 0 ) {
511 if ( isset( $arr[self::META_CONTENT] ) &&
512 isset( $arr[$arr[self::META_CONTENT]] ) &&
513 !( $flags & self::OVERRIDE )
514 ) {
515 throw new RuntimeException(
516 "Attempting to set content element as $name when " . $arr[self::META_CONTENT] .
517 " is already set as the content element"
518 );
519 }
520 $arr[self::META_CONTENT] = $name;
521 }
522
523 /**
524 * Set the name of the content field name (META_CONTENT)
525 *
526 * @since 1.25
527 * @param array|string|null $path See ApiResult::addValue()
528 * @param string|int $name Name of the field
529 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
530 */
531 public function addContentField( $path, $name, $flags = 0 ) {
532 $arr = &$this->path( $path, ( $flags & ApiResult::ADD_ON_TOP ) ? 'prepend' : 'append' );
533 self::setContentField( $arr, $name, $flags );
534 }
535
536 /**
537 * Causes the elements with the specified names to be output as
538 * subelements rather than attributes.
539 * @since 1.25 is static
540 * @param array &$arr
541 * @param array|string|int $names The element name(s) to be output as subelements
542 */
543 public static function setSubelementsList( array &$arr, $names ) {
544 if ( !isset( $arr[self::META_SUBELEMENTS] ) ) {
545 $arr[self::META_SUBELEMENTS] = (array)$names;
546 } else {
547 $arr[self::META_SUBELEMENTS] = array_merge( $arr[self::META_SUBELEMENTS], (array)$names );
548 }
549 }
550
551 /**
552 * Causes the elements with the specified names to be output as
553 * subelements rather than attributes.
554 * @since 1.25
555 * @param array|string|null $path See ApiResult::addValue()
556 * @param array|string|int $names The element name(s) to be output as subelements
557 */
558 public function addSubelementsList( $path, $names ) {
559 $arr = &$this->path( $path );
560 self::setSubelementsList( $arr, $names );
561 }
562
563 /**
564 * Causes the elements with the specified names to be output as
565 * attributes (when possible) rather than as subelements.
566 * @since 1.25
567 * @param array &$arr
568 * @param array|string|int $names The element name(s) to not be output as subelements
569 */
570 public static function unsetSubelementsList( array &$arr, $names ) {
571 if ( isset( $arr[self::META_SUBELEMENTS] ) ) {
572 $arr[self::META_SUBELEMENTS] = array_diff( $arr[self::META_SUBELEMENTS], (array)$names );
573 }
574 }
575
576 /**
577 * Causes the elements with the specified names to be output as
578 * attributes (when possible) rather than as subelements.
579 * @since 1.25
580 * @param array|string|null $path See ApiResult::addValue()
581 * @param array|string|int $names The element name(s) to not be output as subelements
582 */
583 public function removeSubelementsList( $path, $names ) {
584 $arr = &$this->path( $path );
585 self::unsetSubelementsList( $arr, $names );
586 }
587
588 /**
589 * Set the tag name for numeric-keyed values in XML format
590 * @since 1.25 is static
591 * @param array &$arr
592 * @param string $tag Tag name
593 */
594 public static function setIndexedTagName( array &$arr, $tag ) {
595 if ( !is_string( $tag ) ) {
596 throw new InvalidArgumentException( 'Bad tag name' );
597 }
598 $arr[self::META_INDEXED_TAG_NAME] = $tag;
599 }
600
601 /**
602 * Set the tag name for numeric-keyed values in XML format
603 * @since 1.25
604 * @param array|string|null $path See ApiResult::addValue()
605 * @param string $tag Tag name
606 */
607 public function addIndexedTagName( $path, $tag ) {
608 $arr = &$this->path( $path );
609 self::setIndexedTagName( $arr, $tag );
610 }
611
612 /**
613 * Set indexed tag name on $arr and all subarrays
614 *
615 * @since 1.25
616 * @param array &$arr
617 * @param string $tag Tag name
618 */
619 public static function setIndexedTagNameRecursive( array &$arr, $tag ) {
620 if ( !is_string( $tag ) ) {
621 throw new InvalidArgumentException( 'Bad tag name' );
622 }
623 $arr[self::META_INDEXED_TAG_NAME] = $tag;
624 foreach ( $arr as $k => &$v ) {
625 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
626 self::setIndexedTagNameRecursive( $v, $tag );
627 }
628 }
629 }
630
631 /**
632 * Set indexed tag name on $path and all subarrays
633 *
634 * @since 1.25
635 * @param array|string|null $path See ApiResult::addValue()
636 * @param string $tag Tag name
637 */
638 public function addIndexedTagNameRecursive( $path, $tag ) {
639 $arr = &$this->path( $path );
640 self::setIndexedTagNameRecursive( $arr, $tag );
641 }
642
643 /**
644 * Preserve specified keys.
645 *
646 * This prevents XML name mangling and preventing keys from being removed
647 * by self::stripMetadata().
648 *
649 * @since 1.25
650 * @param array &$arr
651 * @param array|string $names The element name(s) to preserve
652 */
653 public static function setPreserveKeysList( array &$arr, $names ) {
654 if ( !isset( $arr[self::META_PRESERVE_KEYS] ) ) {
655 $arr[self::META_PRESERVE_KEYS] = (array)$names;
656 } else {
657 $arr[self::META_PRESERVE_KEYS] = array_merge( $arr[self::META_PRESERVE_KEYS], (array)$names );
658 }
659 }
660
661 /**
662 * Preserve specified keys.
663 * @since 1.25
664 * @see self::setPreserveKeysList()
665 * @param array|string|null $path See ApiResult::addValue()
666 * @param array|string $names The element name(s) to preserve
667 */
668 public function addPreserveKeysList( $path, $names ) {
669 $arr = &$this->path( $path );
670 self::setPreserveKeysList( $arr, $names );
671 }
672
673 /**
674 * Don't preserve specified keys.
675 * @since 1.25
676 * @see self::setPreserveKeysList()
677 * @param array &$arr
678 * @param array|string $names The element name(s) to not preserve
679 */
680 public static function unsetPreserveKeysList( array &$arr, $names ) {
681 if ( isset( $arr[self::META_PRESERVE_KEYS] ) ) {
682 $arr[self::META_PRESERVE_KEYS] = array_diff( $arr[self::META_PRESERVE_KEYS], (array)$names );
683 }
684 }
685
686 /**
687 * Don't preserve specified keys.
688 * @since 1.25
689 * @see self::setPreserveKeysList()
690 * @param array|string|null $path See ApiResult::addValue()
691 * @param array|string $names The element name(s) to not preserve
692 */
693 public function removePreserveKeysList( $path, $names ) {
694 $arr = &$this->path( $path );
695 self::unsetPreserveKeysList( $arr, $names );
696 }
697
698 /**
699 * Set the array data type
700 *
701 * @since 1.25
702 * @param array &$arr
703 * @param string $type See ApiResult::META_TYPE
704 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
705 */
706 public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
707 if ( !in_array( $type, array( 'default', 'array', 'assoc', 'kvp', 'BCarray', 'BCassoc', 'BCkvp' ), true ) ) {
708 throw new InvalidArgumentException( 'Bad type' );
709 }
710 $arr[self::META_TYPE] = $type;
711 if ( is_string( $kvpKeyName ) ) {
712 $arr[self::META_KVP_KEY_NAME] = $kvpKeyName;
713 }
714 }
715
716 /**
717 * Set the array data type for a path
718 * @since 1.25
719 * @param array|string|null $path See ApiResult::addValue()
720 * @param string $type See ApiResult::META_TYPE
721 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
722 */
723 public function addArrayType( $path, $tag, $kvpKeyName = null ) {
724 $arr = &$this->path( $path );
725 self::setArrayType( $arr, $tag, $kvpKeyName );
726 }
727
728 /**
729 * Set the array data type recursively
730 * @since 1.25
731 * @param array &$arr
732 * @param string $type See ApiResult::META_TYPE
733 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
734 */
735 public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
736 self::setArrayType( $arr, $type, $kvpKeyName );
737 foreach ( $arr as $k => &$v ) {
738 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
739 self::setArrayTypeRecursive( $v, $type, $kvpKeyName );
740 }
741 }
742 }
743
744 /**
745 * Set the array data type for a path recursively
746 * @since 1.25
747 * @param array|string|null $path See ApiResult::addValue()
748 * @param string $type See ApiResult::META_TYPE
749 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
750 */
751 public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
752 $arr = &$this->path( $path );
753 self::setArrayTypeRecursive( $arr, $tag, $kvpKeyName );
754 }
755
756 /**@}*/
757
758 /************************************************************************//**
759 * @name Utility
760 * @{
761 */
762
763 /**
764 * Test whether a key should be considered metadata
765 *
766 * @param string $key
767 * @return bool
768 */
769 public static function isMetadataKey( $key ) {
770 return substr( $key, 0, 1 ) === '_';
771 }
772
773 /**
774 * Apply transformations to an array, returning the transformed array.
775 *
776 * @see ApiResult::getResultData()
777 * @since 1.25
778 * @param array $data
779 * @param array $transforms
780 * @return array|object
781 */
782 protected static function applyTransformations( array $dataIn, array $transforms ) {
783 $strip = isset( $transforms['Strip'] ) ? $transforms['Strip'] : 'none';
784 if ( $strip === 'base' ) {
785 $transforms['Strip'] = 'none';
786 }
787 $transformTypes = isset( $transforms['Types'] ) ? $transforms['Types'] : null;
788 if ( $transformTypes !== null && !is_array( $transformTypes ) ) {
789 throw new InvalidArgumentException( __METHOD__ . ':Value for "Types" must be an array' );
790 }
791
792 $metadata = array();
793 $data = self::stripMetadataNonRecursive( $dataIn, $metadata );
794
795 if ( isset( $transforms['Custom'] ) ) {
796 if ( !is_callable( $transforms['Custom'] ) ) {
797 throw new InvalidArgumentException( __METHOD__ . ': Value for "Custom" must be callable' );
798 }
799 call_user_func_array( $transforms['Custom'], array( &$data, &$metadata ) );
800 }
801
802 if ( ( isset( $transforms['BC'] ) || $transformTypes !== null ) &&
803 isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] === 'BCkvp' &&
804 !isset( $metadata[self::META_KVP_KEY_NAME] )
805 ) {
806 throw new UnexpectedValueException( 'Type "BCkvp" used without setting ' .
807 'ApiResult::META_KVP_KEY_NAME metadata item' );
808 }
809
810 // BC transformations
811 $boolKeys = null;
812 $forceKVP = false;
813 if ( isset( $transforms['BC'] ) ) {
814 if ( !is_array( $transforms['BC'] ) ) {
815 throw new InvalidArgumentException( __METHOD__ . ':Value for "BC" must be an array' );
816 }
817 if ( !in_array( 'nobool', $transforms['BC'], true ) ) {
818 $boolKeys = isset( $metadata[self::META_BC_BOOLS] )
819 ? array_flip( $metadata[self::META_BC_BOOLS] )
820 : array();
821 }
822
823 if ( !in_array( 'no*', $transforms['BC'], true ) &&
824 isset( $metadata[self::META_CONTENT] ) && $metadata[self::META_CONTENT] !== '*'
825 ) {
826 $k = $metadata[self::META_CONTENT];
827 $data['*'] = $data[$k];
828 unset( $data[$k] );
829 $metadata[self::META_CONTENT] = '*';
830 }
831
832 if ( !in_array( 'nosub', $transforms['BC'], true ) &&
833 isset( $metadata[self::META_BC_SUBELEMENTS] )
834 ) {
835 foreach ( $metadata[self::META_BC_SUBELEMENTS] as $k ) {
836 $data[$k] = array(
837 '*' => $data[$k],
838 self::META_CONTENT => '*',
839 self::META_TYPE => 'assoc',
840 );
841 }
842 }
843
844 if ( isset( $metadata[self::META_TYPE] ) ) {
845 switch ( $metadata[self::META_TYPE] ) {
846 case 'BCarray':
847 case 'BCassoc':
848 $metadata[self::META_TYPE] = 'default';
849 break;
850 case 'BCkvp':
851 $transformTypes['ArmorKVP'] = $metadata[self::META_KVP_KEY_NAME];
852 break;
853 }
854 }
855 }
856
857 // Figure out type, do recursive calls, and do boolean transform if necessary
858 $defaultType = 'array';
859 $maxKey = -1;
860 foreach ( $data as $k => &$v ) {
861 $v = is_array( $v ) ? self::applyTransformations( $v, $transforms ) : $v;
862 if ( $boolKeys !== null && is_bool( $v ) && !isset( $boolKeys[$k] ) ) {
863 if ( !$v ) {
864 unset( $data[$k] );
865 continue;
866 }
867 $v = '';
868 }
869 if ( is_string( $k ) ) {
870 $defaultType = 'assoc';
871 } elseif ( $k > $maxKey ) {
872 $maxKey = $k;
873 }
874 }
875 unset( $v );
876
877 // Determine which metadata to keep
878 switch ( $strip ) {
879 case 'all':
880 case 'base':
881 $keepMetadata = array();
882 break;
883 case 'none':
884 $keepMetadata = &$metadata;
885 break;
886 case 'bc':
887 $keepMetadata = array_intersect_key( $metadata, array(
888 self::META_INDEXED_TAG_NAME => 1,
889 self::META_SUBELEMENTS => 1,
890 ) );
891 break;
892 default:
893 throw new InvalidArgumentException( __METHOD__ . ': Unknown value for "Strip"' );
894 }
895
896 // Type transformation
897 if ( $transformTypes !== null ) {
898 if ( $defaultType === 'array' && $maxKey !== count( $data ) - 1 ) {
899 $defaultType = 'assoc';
900 }
901
902 // Override type, if provided
903 $type = $defaultType;
904 if ( isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] !== 'default' ) {
905 $type = $metadata[self::META_TYPE];
906 }
907 if ( ( $type === 'kvp' || $type === 'BCkvp' ) &&
908 empty( $transformTypes['ArmorKVP'] )
909 ) {
910 $type = 'assoc';
911 } elseif ( $type === 'BCarray' ) {
912 $type = 'array';
913 } elseif ( $type === 'BCassoc' ) {
914 $type = 'assoc';
915 }
916
917 // Apply transformation
918 switch ( $type ) {
919 case 'assoc':
920 $metadata[self::META_TYPE] = 'assoc';
921 $data += $keepMetadata;
922 return empty( $transformTypes['AssocAsObject'] ) ? $data : (object)$data;
923
924 case 'array':
925 ksort( $data );
926 $data = array_values( $data );
927 $metadata[self::META_TYPE] = 'array';
928 return $data + $keepMetadata;
929
930 case 'kvp':
931 case 'BCkvp':
932 $key = isset( $metadata[self::META_KVP_KEY_NAME] )
933 ? $metadata[self::META_KVP_KEY_NAME]
934 : $transformTypes['ArmorKVP'];
935 $valKey = isset( $transforms['BC'] ) ? '*' : 'value';
936 $assocAsObject = !empty( $transformTypes['AssocAsObject'] );
937
938 $ret = array();
939 foreach ( $data as $k => $v ) {
940 $item = array(
941 $key => $k,
942 $valKey => $v,
943 );
944 if ( $strip === 'none' ) {
945 $item += array(
946 self::META_PRESERVE_KEYS => array( $key ),
947 self::META_CONTENT => $valKey,
948 self::META_TYPE => 'assoc',
949 );
950 }
951 $ret[] = $assocAsObject ? (object)$item : $item;
952 }
953 $metadata[self::META_TYPE] = 'array';
954
955 return $ret + $keepMetadata;
956
957 default:
958 throw new UnexpectedValueException( "Unknown type '$type'" );
959 }
960 } else {
961 return $data + $keepMetadata;
962 }
963 }
964
965 /**
966 * Recursively remove metadata keys from a data array or object
967 *
968 * Note this removes all potential metadata keys, not just the defined
969 * ones.
970 *
971 * @since 1.25
972 * @param array|object $data
973 * @return array|object
974 */
975 public static function stripMetadata( $data ) {
976 if ( is_array( $data ) || is_object( $data ) ) {
977 $isObj = is_object( $data );
978 if ( $isObj ) {
979 $data = (array)$data;
980 }
981 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
982 ? (array)$data[self::META_PRESERVE_KEYS]
983 : array();
984 foreach ( $data as $k => $v ) {
985 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
986 unset( $data[$k] );
987 } elseif ( is_array( $v ) || is_object( $v ) ) {
988 $data[$k] = self::stripMetadata( $v );
989 }
990 }
991 if ( $isObj ) {
992 $data = (object)$data;
993 }
994 }
995 return $data;
996 }
997
998 /**
999 * Remove metadata keys from a data array or object, non-recursive
1000 *
1001 * Note this removes all potential metadata keys, not just the defined
1002 * ones.
1003 *
1004 * @since 1.25
1005 * @param array|object $data
1006 * @param array &$metadata Store metadata here, if provided
1007 * @return array|object
1008 */
1009 public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
1010 if ( !is_array( $metadata ) ) {
1011 $metadata = array();
1012 }
1013 if ( is_array( $data ) || is_object( $data ) ) {
1014 $isObj = is_object( $data );
1015 if ( $isObj ) {
1016 $data = (array)$data;
1017 }
1018 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1019 ? (array)$data[self::META_PRESERVE_KEYS]
1020 : array();
1021 foreach ( $data as $k => $v ) {
1022 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1023 $metadata[$k] = $v;
1024 unset( $data[$k] );
1025 }
1026 }
1027 if ( $isObj ) {
1028 $data = (object)$data;
1029 }
1030 }
1031 return $data;
1032 }
1033
1034 /**
1035 * Get the 'real' size of a result item. This means the strlen() of the item,
1036 * or the sum of the strlen()s of the elements if the item is an array.
1037 * @note Once the deprecated public self::size is removed, we can rename this back to a less awkward name.
1038 * @param mixed $value
1039 * @return int
1040 */
1041 private static function valueSize( $value ) {
1042 $s = 0;
1043 if ( is_array( $value ) ||
1044 is_object( $value ) && !is_callable( array( $value, '__toString' ) )
1045 ) {
1046 foreach ( $value as $k => $v ) {
1047 if ( !self::isMetadataKey( $s ) ) {
1048 $s += self::valueSize( $v );
1049 }
1050 }
1051 } elseif ( is_scalar( $value ) ) {
1052 $s = strlen( $value );
1053 }
1054
1055 return $s;
1056 }
1057
1058 /**
1059 * Return a reference to the internal data at $path
1060 *
1061 * @param array|string|null $path
1062 * @param string $create
1063 * If 'append', append empty arrays.
1064 * If 'prepend', prepend empty arrays.
1065 * If 'dummy', return a dummy array.
1066 * Else, raise an error.
1067 * @return array
1068 */
1069 private function &path( $path, $create = 'append' ) {
1070 $path = (array)$path;
1071 $ret = &$this->data;
1072 foreach ( $path as $i => $k ) {
1073 if ( !isset( $ret[$k] ) ) {
1074 switch ( $create ) {
1075 case 'append':
1076 $ret[$k] = array();
1077 break;
1078 case 'prepend':
1079 $ret = array( $k => array() ) + $ret;
1080 break;
1081 case 'dummy':
1082 $tmp = array();
1083 return $tmp;
1084 default:
1085 $fail = join( '.', array_slice( $path, 0, $i + 1 ) );
1086 throw new InvalidArgumentException( "Path $fail does not exist" );
1087 }
1088 }
1089 if ( !is_array( $ret[$k] ) ) {
1090 $fail = join( '.', array_slice( $path, 0, $i + 1 ) );
1091 throw new InvalidArgumentException( "Path $fail is not an array" );
1092 }
1093 $ret = &$ret[$k];
1094 }
1095 return $ret;
1096 }
1097
1098 /**@}*/
1099
1100 /************************************************************************//**
1101 * @name Deprecated
1102 * @{
1103 */
1104
1105 /**
1106 * Call this function when special elements such as '_element'
1107 * are needed by the formatter, for example in XML printing.
1108 * @deprecated since 1.25, you shouldn't have been using it in the first place
1109 * @since 1.23 $flag parameter added
1110 * @param bool $flag Set the raw mode flag to this state
1111 */
1112 public function setRawMode( $flag = true ) {
1113 // Can't wfDeprecated() here, since we need to set this flag from
1114 // ApiMain for BC with stuff using self::getIsRawMode as
1115 // "self::getIsXMLMode".
1116 $this->isRawMode = $flag;
1117 }
1118
1119 /**
1120 * Returns true whether the formatter requested raw data.
1121 * @deprecated since 1.25, you shouldn't have been using it in the first place
1122 * @return bool
1123 */
1124 public function getIsRawMode() {
1125 /// @todo: After Wikibase stops calling this, warn
1126 return $this->isRawMode;
1127 }
1128
1129 /**
1130 * Get the result's internal data array (read-only)
1131 * @deprecated since 1.25, use $this->getResultData() instead
1132 * @return array
1133 */
1134 public function getData() {
1135 /// @todo: Warn after fixing remaining callers: Wikibase, Gather
1136 return $this->getResultData( null, array(
1137 'BC' => array(),
1138 'Types' => array(),
1139 'Strip' => $this->isRawMode ? 'bc' : 'all',
1140 ) );
1141 }
1142
1143 /**
1144 * Disable size checking in addValue(). Don't use this unless you
1145 * REALLY know what you're doing. Values added while size checking
1146 * was disabled will not be counted (ever)
1147 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1148 */
1149 public function disableSizeCheck() {
1150 wfDeprecated( __METHOD__, '1.24' );
1151 $this->checkingSize = false;
1152 }
1153
1154 /**
1155 * Re-enable size checking in addValue()
1156 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1157 */
1158 public function enableSizeCheck() {
1159 wfDeprecated( __METHOD__, '1.24' );
1160 $this->checkingSize = true;
1161 }
1162
1163 /**
1164 * Alias for self::setValue()
1165 *
1166 * @since 1.21 int $flags replaced boolean $override
1167 * @deprecated since 1.25, use self::setValue() instead
1168 * @param array $arr To add $value to
1169 * @param string $name Index of $arr to add $value at
1170 * @param mixed $value
1171 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
1172 * This parameter used to be boolean, and the value of OVERRIDE=1 was
1173 * specifically chosen so that it would be backwards compatible with the
1174 * new method signature.
1175 */
1176 public static function setElement( &$arr, $name, $value, $flags = 0 ) {
1177 /// @todo: Warn after fixing remaining callers: Wikibase
1178 return self::setValue( $arr, $name, $value, $flags );
1179 }
1180
1181 /**
1182 * Adds a content element to an array.
1183 * Use this function instead of hardcoding the '*' element.
1184 * @deprecated since 1.25, use self::setContentValue() instead
1185 * @param array $arr To add the content element to
1186 * @param mixed $value
1187 * @param string $subElemName When present, content element is created
1188 * as a sub item of $arr. Use this parameter to create elements in
1189 * format "<elem>text</elem>" without attributes.
1190 */
1191 public static function setContent( &$arr, $value, $subElemName = null ) {
1192 /// @todo: Warn after fixing remaining callers: Wikibase
1193 if ( is_array( $value ) ) {
1194 throw new InvalidArgumentException( __METHOD__ . ': Bad parameter' );
1195 }
1196 if ( is_null( $subElemName ) ) {
1197 self::setContentValue( $arr, 'content', $value );
1198 } else {
1199 if ( !isset( $arr[$subElemName] ) ) {
1200 $arr[$subElemName] = array();
1201 }
1202 self::setContentValue( $arr[$subElemName], 'content', $value );
1203 }
1204 }
1205
1206 /**
1207 * Set indexed tag name on all subarrays of $arr
1208 *
1209 * Does not set the tag name for $arr itself.
1210 *
1211 * @deprecated since 1.25, use self::setIndexedTagNameRecursive() instead
1212 * @param array $arr
1213 * @param string $tag Tag name
1214 */
1215 public function setIndexedTagName_recursive( &$arr, $tag ) {
1216 /// @todo: Warn after fixing remaining callers: Wikibase
1217 if ( !is_array( $arr ) ) {
1218 return;
1219 }
1220 self::setIndexedTagNameOnSubarrays( $arr, $tag );
1221 }
1222
1223 /**
1224 * Set indexed tag name on all subarrays of $arr
1225 *
1226 * Does not set the tag name for $arr itself.
1227 *
1228 * @since 1.25
1229 * @deprecated For backwards compatibility, do not use
1230 * @todo: Remove after updating callers to use self::setIndexedTagNameRecursive
1231 * @param array &$arr
1232 * @param string $tag Tag name
1233 */
1234 public static function setIndexedTagNameOnSubarrays( array &$arr, $tag ) {
1235 if ( !is_string( $tag ) ) {
1236 throw new InvalidArgumentException( 'Bad tag name' );
1237 }
1238 foreach ( $arr as $k => &$v ) {
1239 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
1240 $v[self::META_INDEXED_TAG_NAME] = $tag;
1241 self::setIndexedTagNameOnSubarrays( $v, $tag );
1242 }
1243 }
1244 }
1245
1246 /**
1247 * Alias for self::addIndexedTagName()
1248 * @deprecated since 1.25, use $this->addIndexedTagName() instead
1249 * @param array $path Path to the array, like addValue()'s $path
1250 * @param string $tag
1251 */
1252 public function setIndexedTagName_internal( $path, $tag ) {
1253 /// @todo: Warn after fixing remaining callers: Wikibase, Gather
1254 $this->addIndexedTagName( $path, $tag );
1255 }
1256
1257 /**
1258 * Alias for self::addParsedLimit()
1259 * @deprecated since 1.25, use $this->addParsedLimit() instead
1260 * @param string $moduleName
1261 * @param int $limit
1262 */
1263 public function setParsedLimit( $moduleName, $limit ) {
1264 wfDeprecated( __METHOD__, '1.25' );
1265 $this->addParsedLimit( $moduleName, $limit );
1266 }
1267
1268 /**
1269 * Set the ApiMain for use by $this->beginContinuation()
1270 * @since 1.25
1271 * @deprecated for backwards compatibility only, do not use
1272 * @param ApiMain $main
1273 */
1274 public function setMainForContinuation( ApiMain $main ) {
1275 $this->mainForContinuation = $main;
1276 }
1277
1278 /**
1279 * Parse a 'continue' parameter and return status information.
1280 *
1281 * This must be balanced by a call to endContinuation().
1282 *
1283 * @since 1.24
1284 * @deprecated since 1.25, use ApiContinuationManager instead
1285 * @param string|null $continue
1286 * @param ApiBase[] $allModules
1287 * @param array $generatedModules
1288 * @return array
1289 */
1290 public function beginContinuation(
1291 $continue, array $allModules = array(), array $generatedModules = array()
1292 ) {
1293 /// @todo: Warn after fixing remaining callers: Gather
1294 if ( $this->mainForContinuation->getContinuationManager() ) {
1295 throw new UnexpectedValueException(
1296 __METHOD__ . ': Continuation already in progress from ' .
1297 $this->mainForContinuation->getContinuationManager()->getSource()
1298 );
1299 }
1300
1301 // Ugh. If $continue doesn't match that in the request, temporarily
1302 // replace the request when creating the ApiContinuationManager.
1303 if ( $continue === null ) {
1304 $continue = '';
1305 }
1306 if ( $this->mainForContinuation->getVal( 'continue', '' ) !== $continue ) {
1307 $oldCtx = $this->mainForContinuation->getContext();
1308 $newCtx = new DerivativeContext( $oldCtx );
1309 $newCtx->setRequest( new DerivativeRequest(
1310 $oldCtx->getRequest(),
1311 array( 'continue' => $continue ) + $oldCtx->getRequest()->getValues(),
1312 $oldCtx->getRequest()->wasPosted()
1313 ) );
1314 $this->mainForContinuation->setContext( $newCtx );
1315 $reset = new ScopedCallback(
1316 array( $this->mainForContinuation, 'setContext' ),
1317 array( $oldCtx )
1318 );
1319 }
1320 $manager = new ApiContinuationManager(
1321 $this->mainForContinuation, $allModules, $generatedModules
1322 );
1323 $reset = null;
1324
1325 $this->mainForContinuation->setContinuationManager( $manager );
1326
1327 return array(
1328 $manager->isGeneratorDone(),
1329 $manager->getRunModules(),
1330 );
1331 }
1332
1333 /**
1334 * @since 1.24
1335 * @deprecated since 1.25, use ApiContinuationManager instead
1336 * @param ApiBase $module
1337 * @param string $paramName
1338 * @param string|array $paramValue
1339 */
1340 public function setContinueParam( ApiBase $module, $paramName, $paramValue ) {
1341 wfDeprecated( __METHOD__, '1.25' );
1342 if ( $this->mainForContinuation->getContinuationManager() ) {
1343 $this->mainForContinuation->getContinuationManager()->addContinueParam(
1344 $module, $paramName, $paramValue
1345 );
1346 }
1347 }
1348
1349 /**
1350 * @since 1.24
1351 * @deprecated since 1.25, use ApiContinuationManager instead
1352 * @param ApiBase $module
1353 * @param string $paramName
1354 * @param string|array $paramValue
1355 */
1356 public function setGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
1357 wfDeprecated( __METHOD__, '1.25' );
1358 if ( $this->mainForContinuation->getContinuationManager() ) {
1359 $this->mainForContinuation->getContinuationManager()->addGeneratorContinueParam(
1360 $module, $paramName, $paramValue
1361 );
1362 }
1363 }
1364
1365 /**
1366 * Close continuation, writing the data into the result
1367 * @since 1.24
1368 * @deprecated since 1.25, use ApiContinuationManager instead
1369 * @param string $style 'standard' for the new style since 1.21, 'raw' for
1370 * the style used in 1.20 and earlier.
1371 */
1372 public function endContinuation( $style = 'standard' ) {
1373 /// @todo: Warn after fixing remaining callers: Gather
1374 if ( !$this->mainForContinuation->getContinuationManager() ) {
1375 return;
1376 }
1377
1378 if ( $style === 'raw' ) {
1379 $data = $this->mainForContinuation->getContinuationManager()->getRawContinuation();
1380 if ( $data ) {
1381 $this->addValue( null, 'query-continue', $data, self::ADD_ON_TOP | self::NO_SIZE_CHECK );
1382 }
1383 } else {
1384 $this->mainForContinuation->getContinuationManager()->setContinuationIntoResult( $this );
1385 }
1386 }
1387
1388 /**
1389 * No-op, this is now checked on insert.
1390 * @deprecated since 1.25
1391 */
1392 public function cleanUpUTF8() {
1393 wfDeprecated( __METHOD__, '1.25' );
1394 }
1395
1396 /**
1397 * Get the 'real' size of a result item. This means the strlen() of the item,
1398 * or the sum of the strlen()s of the elements if the item is an array.
1399 * @deprecated since 1.25, no external users known and there doesn't seem
1400 * to be any case for such use over just checking the return value from the
1401 * add/set methods.
1402 * @param mixed $value
1403 * @return int
1404 */
1405 public static function size( $value ) {
1406 wfDeprecated( __METHOD__, '1.25' );
1407 return self::valueSize( $value );
1408 }
1409
1410 /**
1411 * Converts a Status object to an array suitable for addValue
1412 * @deprecated since 1.25, use ApiErrorFormatter::arrayFromStatus()
1413 * @param Status $status
1414 * @param string $errorType
1415 * @return array
1416 */
1417 public function convertStatusToArray( $status, $errorType = 'error' ) {
1418 /// @todo: Warn after fixing remaining callers: CentralAuth
1419 return $this->errorFormatter->arrayFromStatus( $status, $errorType );
1420 }
1421
1422 /**
1423 * Alias for self::addIndexedTagName
1424 *
1425 * A bunch of extensions were updated for an earlier version of this
1426 * extension which used this name.
1427 * @deprecated For backwards compatibility, do not use
1428 * @todo: Remove after updating callers to use self::addIndexedTagName
1429 */
1430 public function defineIndexedTagName( $path, $tag ) {
1431 return $this->addIndexedTagName( $path, $tag );
1432 }
1433
1434 /**
1435 * Alias for self::stripMetadata
1436 *
1437 * A bunch of extensions were updated for an earlier version of this
1438 * extension which used this name.
1439 * @deprecated For backwards compatibility, do not use
1440 * @todo: Remove after updating callers to use self::stripMetadata
1441 */
1442 public static function removeMetadata( $data ) {
1443 return self::stripMetadata( $data );
1444 }
1445
1446 /**
1447 * Alias for self::stripMetadataNonRecursive
1448 *
1449 * A bunch of extensions were updated for an earlier version of this
1450 * extension which used this name.
1451 * @deprecated For backwards compatibility, do not use
1452 * @todo: Remove after updating callers to use self::stripMetadataNonRecursive
1453 */
1454 public static function removeMetadataNonRecursive( $data, &$metadata = null ) {
1455 return self::stripMetadataNonRecursive( $data, $metadata );
1456 }
1457
1458 /**
1459 * @deprecated For backwards compatibility, do not use
1460 * @todo: Remove after updating callers
1461 */
1462 public static function transformForBC( array $data ) {
1463 return self::applyTransformations( $data, array(
1464 'BC' => array(),
1465 ) );
1466 }
1467
1468 /**
1469 * @deprecated For backwards compatibility, do not use
1470 * @todo: Remove after updating callers
1471 */
1472 public static function transformForTypes( $data, $options = array() ) {
1473 $transforms = array(
1474 'Types' => array(),
1475 );
1476 if ( isset( $options['assocAsObject'] ) ) {
1477 $transforms['Types']['AssocAsObject'] = $options['assocAsObject'];
1478 }
1479 if ( isset( $options['armorKVP'] ) ) {
1480 $transforms['Types']['ArmorKVP'] = $options['armorKVP'];
1481 }
1482 if ( !empty( $options['BC'] ) ) {
1483 $transforms['BC'] = array( 'nobool', 'no*', 'nosub' );
1484 }
1485 return self::applyTransformations( $data, $transforms );
1486 }
1487
1488 /**@}*/
1489 }
1490
1491 /**
1492 * For really cool vim folding this needs to be at the end:
1493 * vim: foldmarker=@{,@} foldmethod=marker
1494 */