Merge "API: Set HTMLForm when validating field in ApiOptions"
[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 $data[$k] = array(
836 '*' => $data[$k],
837 self::META_CONTENT => '*',
838 self::META_TYPE => 'assoc',
839 );
840 }
841 }
842
843 if ( isset( $metadata[self::META_TYPE] ) ) {
844 switch ( $metadata[self::META_TYPE] ) {
845 case 'BCarray':
846 case 'BCassoc':
847 $metadata[self::META_TYPE] = 'default';
848 break;
849 case 'BCkvp':
850 $transformTypes['ArmorKVP'] = $metadata[self::META_KVP_KEY_NAME];
851 break;
852 }
853 }
854 }
855
856 // Figure out type, do recursive calls, and do boolean transform if necessary
857 $defaultType = 'array';
858 $maxKey = -1;
859 foreach ( $data as $k => &$v ) {
860 $v = is_array( $v ) ? self::applyTransformations( $v, $transforms ) : $v;
861 if ( $boolKeys !== null && is_bool( $v ) && !isset( $boolKeys[$k] ) ) {
862 if ( !$v ) {
863 unset( $data[$k] );
864 continue;
865 }
866 $v = '';
867 }
868 if ( is_string( $k ) ) {
869 $defaultType = 'assoc';
870 } elseif ( $k > $maxKey ) {
871 $maxKey = $k;
872 }
873 }
874 unset( $v );
875
876 // Determine which metadata to keep
877 switch ( $strip ) {
878 case 'all':
879 case 'base':
880 $keepMetadata = array();
881 break;
882 case 'none':
883 $keepMetadata = &$metadata;
884 break;
885 case 'bc':
886 $keepMetadata = array_intersect_key( $metadata, array(
887 self::META_INDEXED_TAG_NAME => 1,
888 self::META_SUBELEMENTS => 1,
889 ) );
890 break;
891 default:
892 throw new InvalidArgumentException( __METHOD__ . ': Unknown value for "Strip"' );
893 }
894
895 // Type transformation
896 if ( $transformTypes !== null ) {
897 if ( $defaultType === 'array' && $maxKey !== count( $data ) - 1 ) {
898 $defaultType = 'assoc';
899 }
900
901 // Override type, if provided
902 $type = $defaultType;
903 if ( isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] !== 'default' ) {
904 $type = $metadata[self::META_TYPE];
905 }
906 if ( ( $type === 'kvp' || $type === 'BCkvp' ) &&
907 empty( $transformTypes['ArmorKVP'] )
908 ) {
909 $type = 'assoc';
910 } elseif ( $type === 'BCarray' ) {
911 $type = 'array';
912 } elseif ( $type === 'BCassoc' ) {
913 $type = 'assoc';
914 }
915
916 // Apply transformation
917 switch ( $type ) {
918 case 'assoc':
919 $metadata[self::META_TYPE] = 'assoc';
920 $data += $keepMetadata;
921 return empty( $transformTypes['AssocAsObject'] ) ? $data : (object)$data;
922
923 case 'array':
924 ksort( $data );
925 $data = array_values( $data );
926 $metadata[self::META_TYPE] = 'array';
927 return $data + $keepMetadata;
928
929 case 'kvp':
930 case 'BCkvp':
931 $key = isset( $metadata[self::META_KVP_KEY_NAME] )
932 ? $metadata[self::META_KVP_KEY_NAME]
933 : $transformTypes['ArmorKVP'];
934 $valKey = isset( $transforms['BC'] ) ? '*' : 'value';
935 $assocAsObject = !empty( $transformTypes['AssocAsObject'] );
936
937 $ret = array();
938 foreach ( $data as $k => $v ) {
939 $item = array(
940 $key => $k,
941 $valKey => $v,
942 );
943 if ( $strip === 'none' ) {
944 $item += array(
945 self::META_PRESERVE_KEYS => array( $key ),
946 self::META_CONTENT => $valKey,
947 self::META_TYPE => 'assoc',
948 );
949 }
950 $ret[] = $assocAsObject ? (object)$item : $item;
951 }
952 $metadata[self::META_TYPE] = 'array';
953
954 return $ret + $keepMetadata;
955
956 default:
957 throw new UnexpectedValueException( "Unknown type '$type'" );
958 }
959 } else {
960 return $data + $keepMetadata;
961 }
962 }
963
964 /**
965 * Recursively remove metadata keys from a data array or object
966 *
967 * Note this removes all potential metadata keys, not just the defined
968 * ones.
969 *
970 * @since 1.25
971 * @param array|object $data
972 * @return array|object
973 */
974 public static function stripMetadata( $data ) {
975 if ( is_array( $data ) || is_object( $data ) ) {
976 $isObj = is_object( $data );
977 if ( $isObj ) {
978 $data = (array)$data;
979 }
980 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
981 ? (array)$data[self::META_PRESERVE_KEYS]
982 : array();
983 foreach ( $data as $k => $v ) {
984 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
985 unset( $data[$k] );
986 } elseif ( is_array( $v ) || is_object( $v ) ) {
987 $data[$k] = self::stripMetadata( $v );
988 }
989 }
990 if ( $isObj ) {
991 $data = (object)$data;
992 }
993 }
994 return $data;
995 }
996
997 /**
998 * Remove metadata keys from a data array or object, non-recursive
999 *
1000 * Note this removes all potential metadata keys, not just the defined
1001 * ones.
1002 *
1003 * @since 1.25
1004 * @param array|object $data
1005 * @param array &$metadata Store metadata here, if provided
1006 * @return array|object
1007 */
1008 public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
1009 if ( !is_array( $metadata ) ) {
1010 $metadata = array();
1011 }
1012 if ( is_array( $data ) || is_object( $data ) ) {
1013 $isObj = is_object( $data );
1014 if ( $isObj ) {
1015 $data = (array)$data;
1016 }
1017 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1018 ? (array)$data[self::META_PRESERVE_KEYS]
1019 : array();
1020 foreach ( $data as $k => $v ) {
1021 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1022 $metadata[$k] = $v;
1023 unset( $data[$k] );
1024 }
1025 }
1026 if ( $isObj ) {
1027 $data = (object)$data;
1028 }
1029 }
1030 return $data;
1031 }
1032
1033 /**
1034 * Get the 'real' size of a result item. This means the strlen() of the item,
1035 * or the sum of the strlen()s of the elements if the item is an array.
1036 * @note Once the deprecated public self::size is removed, we can rename this back to a less awkward name.
1037 * @param mixed $value
1038 * @return int
1039 */
1040 private static function valueSize( $value ) {
1041 $s = 0;
1042 if ( is_array( $value ) ||
1043 is_object( $value ) && !is_callable( array( $value, '__toString' ) )
1044 ) {
1045 foreach ( $value as $k => $v ) {
1046 if ( !self::isMetadataKey( $s ) ) {
1047 $s += self::valueSize( $v );
1048 }
1049 }
1050 } elseif ( is_scalar( $value ) ) {
1051 $s = strlen( $value );
1052 }
1053
1054 return $s;
1055 }
1056
1057 /**
1058 * Return a reference to the internal data at $path
1059 *
1060 * @param array|string|null $path
1061 * @param string $create
1062 * If 'append', append empty arrays.
1063 * If 'prepend', prepend empty arrays.
1064 * If 'dummy', return a dummy array.
1065 * Else, raise an error.
1066 * @return array
1067 */
1068 private function &path( $path, $create = 'append' ) {
1069 $path = (array)$path;
1070 $ret = &$this->data;
1071 foreach ( $path as $i => $k ) {
1072 if ( !isset( $ret[$k] ) ) {
1073 switch ( $create ) {
1074 case 'append':
1075 $ret[$k] = array();
1076 break;
1077 case 'prepend':
1078 $ret = array( $k => array() ) + $ret;
1079 break;
1080 case 'dummy':
1081 $tmp = array();
1082 return $tmp;
1083 default:
1084 $fail = join( '.', array_slice( $path, 0, $i + 1 ) );
1085 throw new InvalidArgumentException( "Path $fail does not exist" );
1086 }
1087 }
1088 if ( !is_array( $ret[$k] ) ) {
1089 $fail = join( '.', array_slice( $path, 0, $i + 1 ) );
1090 throw new InvalidArgumentException( "Path $fail is not an array" );
1091 }
1092 $ret = &$ret[$k];
1093 }
1094 return $ret;
1095 }
1096
1097 /**@}*/
1098
1099 /************************************************************************//**
1100 * @name Deprecated
1101 * @{
1102 */
1103
1104 /**
1105 * Call this function when special elements such as '_element'
1106 * are needed by the formatter, for example in XML printing.
1107 * @deprecated since 1.25, you shouldn't have been using it in the first place
1108 * @since 1.23 $flag parameter added
1109 * @param bool $flag Set the raw mode flag to this state
1110 */
1111 public function setRawMode( $flag = true ) {
1112 // Can't wfDeprecated() here, since we need to set this flag from
1113 // ApiMain for BC with stuff using self::getIsRawMode as
1114 // "self::getIsXMLMode".
1115 $this->isRawMode = $flag;
1116 }
1117
1118 /**
1119 * Returns true whether the formatter requested raw data.
1120 * @deprecated since 1.25, you shouldn't have been using it in the first place
1121 * @return bool
1122 */
1123 public function getIsRawMode() {
1124 /// @todo: After Wikibase stops calling this, warn
1125 return $this->isRawMode;
1126 }
1127
1128 /**
1129 * Get the result's internal data array (read-only)
1130 * @deprecated since 1.25, use $this->getResultData() instead
1131 * @return array
1132 */
1133 public function getData() {
1134 wfDeprecated( __METHOD__, '1.25' );
1135 return $this->getResultData( null, array(
1136 'BC' => array(),
1137 'Types' => array(),
1138 'Strip' => $this->isRawMode ? 'bc' : 'all',
1139 ) );
1140 }
1141
1142 /**
1143 * Disable size checking in addValue(). Don't use this unless you
1144 * REALLY know what you're doing. Values added while size checking
1145 * was disabled will not be counted (ever)
1146 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1147 */
1148 public function disableSizeCheck() {
1149 wfDeprecated( __METHOD__, '1.24' );
1150 $this->checkingSize = false;
1151 }
1152
1153 /**
1154 * Re-enable size checking in addValue()
1155 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1156 */
1157 public function enableSizeCheck() {
1158 wfDeprecated( __METHOD__, '1.24' );
1159 $this->checkingSize = true;
1160 }
1161
1162 /**
1163 * Alias for self::setValue()
1164 *
1165 * @since 1.21 int $flags replaced boolean $override
1166 * @deprecated since 1.25, use self::setValue() instead
1167 * @param array $arr To add $value to
1168 * @param string $name Index of $arr to add $value at
1169 * @param mixed $value
1170 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
1171 * This parameter used to be boolean, and the value of OVERRIDE=1 was
1172 * specifically chosen so that it would be backwards compatible with the
1173 * new method signature.
1174 */
1175 public static function setElement( &$arr, $name, $value, $flags = 0 ) {
1176 wfDeprecated( __METHOD__, '1.25' );
1177 return self::setValue( $arr, $name, $value, $flags );
1178 }
1179
1180 /**
1181 * Adds a content element to an array.
1182 * Use this function instead of hardcoding the '*' element.
1183 * @deprecated since 1.25, use self::setContentValue() instead
1184 * @param array $arr To add the content element to
1185 * @param mixed $value
1186 * @param string $subElemName When present, content element is created
1187 * as a sub item of $arr. Use this parameter to create elements in
1188 * format "<elem>text</elem>" without attributes.
1189 */
1190 public static function setContent( &$arr, $value, $subElemName = null ) {
1191 wfDeprecated( __METHOD__, '1.25' );
1192 if ( is_array( $value ) ) {
1193 throw new InvalidArgumentException( __METHOD__ . ': Bad parameter' );
1194 }
1195 if ( is_null( $subElemName ) ) {
1196 self::setContentValue( $arr, 'content', $value );
1197 } else {
1198 if ( !isset( $arr[$subElemName] ) ) {
1199 $arr[$subElemName] = array();
1200 }
1201 self::setContentValue( $arr[$subElemName], 'content', $value );
1202 }
1203 }
1204
1205 /**
1206 * Set indexed tag name on all subarrays of $arr
1207 *
1208 * Does not set the tag name for $arr itself.
1209 *
1210 * @deprecated since 1.25, use self::setIndexedTagNameRecursive() instead
1211 * @param array $arr
1212 * @param string $tag Tag name
1213 */
1214 public function setIndexedTagName_recursive( &$arr, $tag ) {
1215 wfDeprecated( __METHOD__, '1.25' );
1216 if ( !is_array( $arr ) ) {
1217 return;
1218 }
1219 if ( !is_string( $tag ) ) {
1220 throw new InvalidArgumentException( 'Bad tag name' );
1221 }
1222 foreach ( $arr as $k => &$v ) {
1223 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
1224 $v[self::META_INDEXED_TAG_NAME] = $tag;
1225 $this->setIndexedTagName_recursive( $v, $tag );
1226 }
1227 }
1228 }
1229
1230 /**
1231 * Alias for self::addIndexedTagName()
1232 * @deprecated since 1.25, use $this->addIndexedTagName() instead
1233 * @param array $path Path to the array, like addValue()'s $path
1234 * @param string $tag
1235 */
1236 public function setIndexedTagName_internal( $path, $tag ) {
1237 wfDeprecated( __METHOD__, '1.25' );
1238 $this->addIndexedTagName( $path, $tag );
1239 }
1240
1241 /**
1242 * Alias for self::addParsedLimit()
1243 * @deprecated since 1.25, use $this->addParsedLimit() instead
1244 * @param string $moduleName
1245 * @param int $limit
1246 */
1247 public function setParsedLimit( $moduleName, $limit ) {
1248 wfDeprecated( __METHOD__, '1.25' );
1249 $this->addParsedLimit( $moduleName, $limit );
1250 }
1251
1252 /**
1253 * Set the ApiMain for use by $this->beginContinuation()
1254 * @since 1.25
1255 * @deprecated for backwards compatibility only, do not use
1256 * @param ApiMain $main
1257 */
1258 public function setMainForContinuation( ApiMain $main ) {
1259 $this->mainForContinuation = $main;
1260 }
1261
1262 /**
1263 * Parse a 'continue' parameter and return status information.
1264 *
1265 * This must be balanced by a call to endContinuation().
1266 *
1267 * @since 1.24
1268 * @deprecated since 1.25, use ApiContinuationManager instead
1269 * @param string|null $continue
1270 * @param ApiBase[] $allModules
1271 * @param array $generatedModules
1272 * @return array
1273 */
1274 public function beginContinuation(
1275 $continue, array $allModules = array(), array $generatedModules = array()
1276 ) {
1277 wfDeprecated( __METHOD__, '1.25' );
1278 if ( $this->mainForContinuation->getContinuationManager() ) {
1279 throw new UnexpectedValueException(
1280 __METHOD__ . ': Continuation already in progress from ' .
1281 $this->mainForContinuation->getContinuationManager()->getSource()
1282 );
1283 }
1284
1285 // Ugh. If $continue doesn't match that in the request, temporarily
1286 // replace the request when creating the ApiContinuationManager.
1287 if ( $continue === null ) {
1288 $continue = '';
1289 }
1290 if ( $this->mainForContinuation->getVal( 'continue', '' ) !== $continue ) {
1291 $oldCtx = $this->mainForContinuation->getContext();
1292 $newCtx = new DerivativeContext( $oldCtx );
1293 $newCtx->setRequest( new DerivativeRequest(
1294 $oldCtx->getRequest(),
1295 array( 'continue' => $continue ) + $oldCtx->getRequest()->getValues(),
1296 $oldCtx->getRequest()->wasPosted()
1297 ) );
1298 $this->mainForContinuation->setContext( $newCtx );
1299 $reset = new ScopedCallback(
1300 array( $this->mainForContinuation, 'setContext' ),
1301 array( $oldCtx )
1302 );
1303 }
1304 $manager = new ApiContinuationManager(
1305 $this->mainForContinuation, $allModules, $generatedModules
1306 );
1307 $reset = null;
1308
1309 $this->mainForContinuation->setContinuationManager( $manager );
1310
1311 return array(
1312 $manager->isGeneratorDone(),
1313 $manager->getRunModules(),
1314 );
1315 }
1316
1317 /**
1318 * @since 1.24
1319 * @deprecated since 1.25, use ApiContinuationManager instead
1320 * @param ApiBase $module
1321 * @param string $paramName
1322 * @param string|array $paramValue
1323 */
1324 public function setContinueParam( ApiBase $module, $paramName, $paramValue ) {
1325 wfDeprecated( __METHOD__, '1.25' );
1326 if ( $this->mainForContinuation->getContinuationManager() ) {
1327 $this->mainForContinuation->getContinuationManager()->addContinueParam(
1328 $module, $paramName, $paramValue
1329 );
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 setGeneratorContinueParam( ApiBase $module, $paramName, $paramValue ) {
1341 wfDeprecated( __METHOD__, '1.25' );
1342 if ( $this->mainForContinuation->getContinuationManager() ) {
1343 $this->mainForContinuation->getContinuationManager()->addGeneratorContinueParam(
1344 $module, $paramName, $paramValue
1345 );
1346 }
1347 }
1348
1349 /**
1350 * Close continuation, writing the data into the result
1351 * @since 1.24
1352 * @deprecated since 1.25, use ApiContinuationManager instead
1353 * @param string $style 'standard' for the new style since 1.21, 'raw' for
1354 * the style used in 1.20 and earlier.
1355 */
1356 public function endContinuation( $style = 'standard' ) {
1357 wfDeprecated( __METHOD__, '1.25' );
1358 if ( !$this->mainForContinuation->getContinuationManager() ) {
1359 return;
1360 }
1361
1362 if ( $style === 'raw' ) {
1363 $data = $this->mainForContinuation->getContinuationManager()->getRawContinuation();
1364 if ( $data ) {
1365 $this->addValue( null, 'query-continue', $data, self::ADD_ON_TOP | self::NO_SIZE_CHECK );
1366 }
1367 } else {
1368 $this->mainForContinuation->getContinuationManager()->setContinuationIntoResult( $this );
1369 }
1370 }
1371
1372 /**
1373 * No-op, this is now checked on insert.
1374 * @deprecated since 1.25
1375 */
1376 public function cleanUpUTF8() {
1377 wfDeprecated( __METHOD__, '1.25' );
1378 }
1379
1380 /**
1381 * Get the 'real' size of a result item. This means the strlen() of the item,
1382 * or the sum of the strlen()s of the elements if the item is an array.
1383 * @deprecated since 1.25, no external users known and there doesn't seem
1384 * to be any case for such use over just checking the return value from the
1385 * add/set methods.
1386 * @param mixed $value
1387 * @return int
1388 */
1389 public static function size( $value ) {
1390 wfDeprecated( __METHOD__, '1.25' );
1391 return self::valueSize( $value );
1392 }
1393
1394 /**
1395 * Converts a Status object to an array suitable for addValue
1396 * @deprecated since 1.25, use ApiErrorFormatter::arrayFromStatus()
1397 * @param Status $status
1398 * @param string $errorType
1399 * @return array
1400 */
1401 public function convertStatusToArray( $status, $errorType = 'error' ) {
1402 wfDeprecated( __METHOD__, '1.25' );
1403 return $this->errorFormatter->arrayFromStatus( $status, $errorType );
1404 }
1405
1406 /**@}*/
1407 }
1408
1409 /**
1410 * For really cool vim folding this needs to be at the end:
1411 * vim: foldmarker=@{,@} foldmethod=marker
1412 */