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