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