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