345cf43a2f163bc39b308ecd201b767b601c7bf4
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.
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.
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
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.
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.
30 * @since 1.25 this is no longer a subclass of ApiBase
33 class ApiResult
implements ApiSerializable
{
36 * Override existing value in addValue(), setValue(), and similar functions
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.
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.
56 const NO_SIZE_CHECK
= 4;
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
64 const NO_VALIDATE
= 12;
67 * Key for the 'indexed tag name' metadata item. Value is string.
70 const META_INDEXED_TAG_NAME
= '_element';
73 * Key for the 'subelements' metadata item. Value is string[].
76 const META_SUBELEMENTS
= '_subelements';
79 * Key for the 'preserve keys' metadata item. Value is string[].
82 const META_PRESERVE_KEYS
= '_preservekeys';
85 * Key for the 'content' metadata item. Value is string.
88 const META_CONTENT
= '_content';
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.
108 const META_TYPE
= '_type';
111 * Key (rather than "name" or other default) for when META_TYPE is 'kvp' or
112 * 'BCkvp'. Value is string.
115 const META_KVP_KEY_NAME
= '_kvpkeyname';
118 * Key for the 'BC bools' metadata item. Value is string[].
119 * Note no setter is provided.
122 const META_BC_BOOLS
= '_BC_bools';
125 * Key for the 'BC subelements' metadata item. Value is string[].
126 * Note no setter is provided.
129 const META_BC_SUBELEMENTS
= '_BC_subelements';
131 private $data, $size, $maxSize;
132 private $errorFormatter;
135 private $isRawMode, $checkingSize, $mainForContinuation;
138 * @param int|bool $maxSize Maximum result "size", or false for no limit
139 * @since 1.25 Takes an integer|bool rather than an ApiMain
141 public function __construct( $maxSize ) {
142 if ( $maxSize instanceof ApiMain
) {
143 wfDeprecated( 'ApiMain to ' . __METHOD__
, '1.25' );
144 $this->errorFormatter
= $maxSize->getErrorFormatter();
145 $this->mainForContinuation
= $maxSize;
146 $maxSize = $maxSize->getConfig()->get( 'APIMaxResultSize' );
149 $this->maxSize
= $maxSize;
150 $this->isRawMode
= false;
151 $this->checkingSize
= true;
156 * Set the error formatter
158 * @param ApiErrorFormatter $formatter
160 public function setErrorFormatter( ApiErrorFormatter
$formatter ) {
161 $this->errorFormatter
= $formatter;
165 * Allow for adding one ApiResult into another
169 public function serializeForApiResult() {
173 /************************************************************************//**
179 * Clear the current result data.
181 public function reset() {
183 self
::META_TYPE
=> 'assoc', // Usually what's desired
189 * Get the result data array
191 * The returned value should be considered read-only.
193 * Transformations include:
195 * Custom: (callable) Applied before other transformations. Signature is
196 * function ( &$data, &$metadata ), return value is ignored. Called for
199 * BC: (array) This transformation does various adjustments to bring the
200 * output in line with the pre-1.25 result format. The value array is a
201 * list of flags: 'nobool', 'no*', 'nosub'.
202 * - Boolean-valued items are changed to '' if true or removed if false,
203 * unless listed in META_BC_BOOLS. This may be skipped by including
204 * 'nobool' in the value array.
205 * - The tag named by META_CONTENT is renamed to '*', and META_CONTENT is
206 * set to '*'. This may be skipped by including 'no*' in the value
208 * - Tags listed in META_BC_SUBELEMENTS will have their values changed to
209 * array( '*' => $value ). This may be skipped by including 'nosub' in
211 * - If META_TYPE is 'BCarray', set it to 'default'
212 * - If META_TYPE is 'BCassoc', set it to 'default'
213 * - If META_TYPE is 'BCkvp', perform the transformation (even if
214 * the Types transformation is not being applied).
216 * Types: (assoc) Apply transformations based on META_TYPE. The values
217 * array is an associative array with the following possible keys:
218 * - AssocAsObject: (bool) If true, return arrays with META_TYPE 'assoc'
220 * - ArmorKVP: (string) If provided, transform arrays with META_TYPE 'kvp'
221 * and 'BCkvp' into arrays of two-element arrays, something like this:
223 * foreach ( $input as $key => $value ) {
225 * $pair[$META_KVP_KEY_NAME ?: $ArmorKVP_value] = $key;
226 * ApiResult::setContentValue( $pair, 'value', $value );
230 * Strip: (string) Strips metadata keys from the result.
231 * - 'all': Strip all metadata, recursively
232 * - 'base': Strip metadata at the top-level only.
233 * - 'none': Do not strip metadata.
234 * - 'bc': Like 'all', but leave certain pre-1.25 keys.
237 * @param array|string|null $path Path to fetch, see ApiResult::addValue
238 * @param array $transforms See above
239 * @return mixed Result data, or null if not found
241 public function getResultData( $path = array(), $transforms = array() ) {
242 $path = (array)$path;
244 return self
::applyTransformations( $this->data
, $transforms );
247 $last = array_pop( $path );
248 $ret = &$this->path( $path, 'dummy' );
249 if ( !isset( $ret[$last] ) ) {
251 } elseif ( is_array( $ret[$last] ) ) {
252 return self
::applyTransformations( $ret[$last], $transforms );
259 * Get the size of the result, i.e. the amount of bytes in it
262 public function getSize() {
267 * Add an output value to the array by name.
269 * Verifies that value with the same name has not been added before.
272 * @param array &$arr To add $value to
273 * @param string|int|null $name Index of $arr to add $value at,
274 * or null to use the next numeric index.
275 * @param mixed $value
276 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
278 public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
279 if ( $name === null ) {
280 if ( $flags & ApiResult
::ADD_ON_TOP
) {
281 array_unshift( $arr, $value );
283 array_push( $arr, $value );
288 if ( !( $flags & ApiResult
::NO_VALIDATE
) ) {
289 $value = self
::validateValue( $value );
292 $exists = isset( $arr[$name] );
293 if ( !$exists ||
( $flags & ApiResult
::OVERRIDE
) ) {
294 if ( !$exists && ( $flags & ApiResult
::ADD_ON_TOP
) ) {
295 $arr = array( $name => $value ) +
$arr;
297 $arr[$name] = $value;
299 } elseif ( is_array( $arr[$name] ) && is_array( $value ) ) {
300 $conflicts = array_intersect_key( $arr[$name], $value );
302 $arr[$name] +
= $value;
304 $keys = join( ', ', array_keys( $conflicts ) );
305 throw new RuntimeException( "Conflicting keys ($keys) when attempting to merge element $name" );
308 throw new RuntimeException( "Attempting to add element $name=$value, existing value is {$arr[$name]}" );
313 * Validate a value for addition to the result
314 * @param mixed $value
316 private static function validateValue( $value ) {
319 if ( is_object( $value ) ) {
320 // Note we use is_callable() here instead of instanceof because
321 // ApiSerializable is an informal protocol (see docs there for details).
322 if ( is_callable( array( $value, 'serializeForApiResult' ) ) ) {
324 $value = $value->serializeForApiResult();
325 if ( is_object( $value ) ) {
326 throw new UnexpectedValueException(
327 get_class( $oldValue ) . "::serializeForApiResult() returned an object of class " .
332 // Recursive call instead of fall-through so we can throw a
333 // better exception message.
335 return self
::validateValue( $value );
336 } catch ( Exception
$ex ) {
337 throw new UnexpectedValueException(
338 get_class( $oldValue ) . "::serializeForApiResult() returned an invalid value: " .
344 } elseif ( is_callable( array( $value, '__toString' ) ) ) {
345 $value = (string)$value;
347 $value = (array)$value +
array( self
::META_TYPE
=> 'assoc' );
350 if ( is_array( $value ) ) {
351 foreach ( $value as $k => $v ) {
352 $value[$k] = self
::validateValue( $v );
354 } elseif ( is_float( $value ) && !is_finite( $value ) ) {
355 throw new InvalidArgumentException( "Cannot add non-finite floats to ApiResult" );
356 } elseif ( is_string( $value ) ) {
357 $value = $wgContLang->normalize( $value );
358 } elseif ( $value !== null && !is_scalar( $value ) ) {
359 $type = gettype( $value );
360 if ( is_resource( $value ) ) {
361 $type .= '(' . get_resource_type( $value ) . ')';
363 throw new InvalidArgumentException( "Cannot add $type to ApiResult" );
370 * Add value to the output data at the given path.
372 * Path can be an indexed array, each element specifying the branch at which to add the new
373 * value. Setting $path to array('a','b','c') is equivalent to data['a']['b']['c'] = $value.
374 * If $path is null, the value will be inserted at the data root.
376 * @param array|string|int|null $path
377 * @param string|int|null $name See ApiResult::setValue()
378 * @param mixed $value
379 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
380 * This parameter used to be boolean, and the value of OVERRIDE=1 was specifically
381 * chosen so that it would be backwards compatible with the new method signature.
382 * @return bool True if $value fits in the result, false if not
383 * @since 1.21 int $flags replaced boolean $override
385 public function addValue( $path, $name, $value, $flags = 0 ) {
386 $arr = &$this->path( $path, ( $flags & ApiResult
::ADD_ON_TOP
) ?
'prepend' : 'append' );
388 if ( $this->checkingSize
&& !( $flags & ApiResult
::NO_SIZE_CHECK
) ) {
389 $newsize = $this->size + self
::valueSize( $value );
390 if ( $this->maxSize
!== false && $newsize > $this->maxSize
) {
391 /// @todo Add i18n message when replacing calls to ->setWarning()
392 $msg = new ApiRawMessage( 'This result was truncated because it would otherwise ' .
393 ' be larger than the limit of $1 bytes', 'truncatedresult' );
394 $msg->numParams( $this->maxSize
);
395 $this->errorFormatter
->addWarning( 'result', $msg );
398 $this->size
= $newsize;
401 self
::setValue( $arr, $name, $value, $flags );
406 * Remove an output value to the array by name.
407 * @param array &$arr To remove $value from
408 * @param string|int $name Index of $arr to remove
409 * @return mixed Old value, or null
411 public static function unsetValue( array &$arr, $name ) {
413 if ( isset( $arr[$name] ) ) {
415 unset( $arr[$name] );
421 * Remove value from the output data at the given path.
424 * @param array|string|null $path See ApiResult::addValue()
425 * @param string|int|null $name Index to remove at $path.
426 * If null, $path itself is removed.
427 * @param int $flags Flags used when adding the value
428 * @return mixed Old value, or null
430 public function removeValue( $path, $name, $flags = 0 ) {
431 $path = (array)$path;
432 if ( $name === null ) {
434 throw new InvalidArgumentException( 'Cannot remove the data root' );
436 $name = array_pop( $path );
438 $ret = self
::unsetValue( $this->path( $path, 'dummy' ), $name );
439 if ( $this->checkingSize
&& !( $flags & ApiResult
::NO_SIZE_CHECK
) ) {
440 $newsize = $this->size
- self
::valueSize( $ret );
441 $this->size
= max( $newsize, 0 );
447 * Add an output value to the array by name and mark as META_CONTENT.
450 * @param array &$arr To add $value to
451 * @param string|int $name Index of $arr to add $value at.
452 * @param mixed $value
453 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
455 public static function setContentValue( array &$arr, $name, $value, $flags = 0 ) {
456 if ( $name === null ) {
457 throw new InvalidArgumentException( 'Content value must be named' );
459 self
::setContentField( $arr, $name, $flags );
460 self
::setValue( $arr, $name, $value, $flags );
464 * Add value to the output data at the given path and mark as META_CONTENT
467 * @param array|string|null $path See ApiResult::addValue()
468 * @param string|int $name See ApiResult::setValue()
469 * @param mixed $value
470 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
471 * @return bool True if $value fits in the result, false if not
473 public function addContentValue( $path, $name, $value, $flags = 0 ) {
474 if ( $name === null ) {
475 throw new InvalidArgumentException( 'Content value must be named' );
477 $this->addContentField( $path, $name, $flags );
478 $this->addValue( $path, $name, $value, $flags );
482 * Add the numeric limit for a limit=max to the result.
485 * @param string $moduleName
488 public function addParsedLimit( $moduleName, $limit ) {
489 // Add value, allowing overwriting
490 $this->addValue( 'limits', $moduleName, $limit,
491 ApiResult
::OVERRIDE | ApiResult
::NO_SIZE_CHECK
);
496 /************************************************************************//**
502 * Set the name of the content field name (META_CONTENT)
506 * @param string|int $name Name of the field
507 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
509 public static function setContentField( array &$arr, $name, $flags = 0 ) {
510 if ( isset( $arr[self
::META_CONTENT
] ) &&
511 isset( $arr[$arr[self
::META_CONTENT
]] ) &&
512 !( $flags & self
::OVERRIDE
)
514 throw new RuntimeException(
515 "Attempting to set content element as $name when " . $arr[self
::META_CONTENT
] .
516 " is already set as the content element"
519 $arr[self
::META_CONTENT
] = $name;
523 * Set the name of the content field name (META_CONTENT)
526 * @param array|string|null $path See ApiResult::addValue()
527 * @param string|int $name Name of the field
528 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
530 public function addContentField( $path, $name, $flags = 0 ) {
531 $arr = &$this->path( $path, ( $flags & ApiResult
::ADD_ON_TOP
) ?
'prepend' : 'append' );
532 self
::setContentField( $arr, $name, $flags );
536 * Causes the elements with the specified names to be output as
537 * subelements rather than attributes.
538 * @since 1.25 is static
540 * @param array|string|int $names The element name(s) to be output as subelements
542 public static function setSubelementsList( array &$arr, $names ) {
543 if ( !isset( $arr[self
::META_SUBELEMENTS
] ) ) {
544 $arr[self
::META_SUBELEMENTS
] = (array)$names;
546 $arr[self
::META_SUBELEMENTS
] = array_merge( $arr[self
::META_SUBELEMENTS
], (array)$names );
551 * Causes the elements with the specified names to be output as
552 * subelements rather than attributes.
554 * @param array|string|null $path See ApiResult::addValue()
555 * @param array|string|int $names The element name(s) to be output as subelements
557 public function addSubelementsList( $path, $names ) {
558 $arr = &$this->path( $path );
559 self
::setSubelementsList( $arr, $names );
563 * Causes the elements with the specified names to be output as
564 * attributes (when possible) rather than as subelements.
567 * @param array|string|int $names The element name(s) to not be output as subelements
569 public static function unsetSubelementsList( array &$arr, $names ) {
570 if ( isset( $arr[self
::META_SUBELEMENTS
] ) ) {
571 $arr[self
::META_SUBELEMENTS
] = array_diff( $arr[self
::META_SUBELEMENTS
], (array)$names );
576 * Causes the elements with the specified names to be output as
577 * attributes (when possible) rather than as subelements.
579 * @param array|string|null $path See ApiResult::addValue()
580 * @param array|string|int $names The element name(s) to not be output as subelements
582 public function removeSubelementsList( $path, $names ) {
583 $arr = &$this->path( $path );
584 self
::unsetSubelementsList( $arr, $names );
588 * Set the tag name for numeric-keyed values in XML format
589 * @since 1.25 is static
591 * @param string $tag Tag name
593 public static function setIndexedTagName( array &$arr, $tag ) {
594 if ( !is_string( $tag ) ) {
595 throw new InvalidArgumentException( 'Bad tag name' );
597 $arr[self
::META_INDEXED_TAG_NAME
] = $tag;
601 * Set the tag name for numeric-keyed values in XML format
603 * @param array|string|null $path See ApiResult::addValue()
604 * @param string $tag Tag name
606 public function addIndexedTagName( $path, $tag ) {
607 $arr = &$this->path( $path );
608 self
::setIndexedTagName( $arr, $tag );
612 * Set indexed tag name on $arr and all subarrays
616 * @param string $tag Tag name
618 public static function setIndexedTagNameRecursive( array &$arr, $tag ) {
619 if ( !is_string( $tag ) ) {
620 throw new InvalidArgumentException( 'Bad tag name' );
622 $arr[self
::META_INDEXED_TAG_NAME
] = $tag;
623 foreach ( $arr as $k => &$v ) {
624 if ( !self
::isMetadataKey( $k ) && is_array( $v ) ) {
625 self
::setIndexedTagNameRecursive( $v, $tag );
631 * Set indexed tag name on $path and all subarrays
634 * @param array|string|null $path See ApiResult::addValue()
635 * @param string $tag Tag name
637 public function addIndexedTagNameRecursive( $path, $tag ) {
638 $arr = &$this->path( $path );
639 self
::setIndexedTagNameRecursive( $arr, $tag );
643 * Preserve specified keys.
645 * This prevents XML name mangling and preventing keys from being removed
646 * by self::stripMetadata().
650 * @param array|string $names The element name(s) to preserve
652 public static function setPreserveKeysList( array &$arr, $names ) {
653 if ( !isset( $arr[self
::META_PRESERVE_KEYS
] ) ) {
654 $arr[self
::META_PRESERVE_KEYS
] = (array)$names;
656 $arr[self
::META_PRESERVE_KEYS
] = array_merge( $arr[self
::META_PRESERVE_KEYS
], (array)$names );
661 * Preserve specified keys.
663 * @see self::setPreserveKeysList()
664 * @param array|string|null $path See ApiResult::addValue()
665 * @param array|string $names The element name(s) to preserve
667 public function addPreserveKeysList( $path, $names ) {
668 $arr = &$this->path( $path );
669 self
::setPreserveKeysList( $arr, $names );
673 * Don't preserve specified keys.
675 * @see self::setPreserveKeysList()
677 * @param array|string $names The element name(s) to not preserve
679 public static function unsetPreserveKeysList( array &$arr, $names ) {
680 if ( isset( $arr[self
::META_PRESERVE_KEYS
] ) ) {
681 $arr[self
::META_PRESERVE_KEYS
] = array_diff( $arr[self
::META_PRESERVE_KEYS
], (array)$names );
686 * Don't preserve specified keys.
688 * @see self::setPreserveKeysList()
689 * @param array|string|null $path See ApiResult::addValue()
690 * @param array|string $names The element name(s) to not preserve
692 public function removePreserveKeysList( $path, $names ) {
693 $arr = &$this->path( $path );
694 self
::unsetPreserveKeysList( $arr, $names );
698 * Set the array data type
702 * @param string $type See ApiResult::META_TYPE
703 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
705 public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
706 if ( !in_array( $type, array( 'default', 'array', 'assoc', 'kvp', 'BCarray', 'BCassoc', 'BCkvp' ), true ) ) {
707 throw new InvalidArgumentException( 'Bad type' );
709 $arr[self
::META_TYPE
] = $type;
710 if ( is_string( $kvpKeyName ) ) {
711 $arr[self
::META_KVP_KEY_NAME
] = $kvpKeyName;
716 * Set the array data type for a path
718 * @param array|string|null $path See ApiResult::addValue()
719 * @param string $type See ApiResult::META_TYPE
720 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
722 public function addArrayType( $path, $tag, $kvpKeyName = null ) {
723 $arr = &$this->path( $path );
724 self
::setArrayType( $arr, $tag, $kvpKeyName );
728 * Set the array data type recursively
731 * @param string $type See ApiResult::META_TYPE
732 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
734 public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
735 self
::setArrayType( $arr, $type, $kvpKeyName );
736 foreach ( $arr as $k => &$v ) {
737 if ( !self
::isMetadataKey( $k ) && is_array( $v ) ) {
738 self
::setArrayTypeRecursive( $v, $type, $kvpKeyName );
744 * Set the array data type for a path recursively
746 * @param array|string|null $path See ApiResult::addValue()
747 * @param string $type See ApiResult::META_TYPE
748 * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
750 public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
751 $arr = &$this->path( $path );
752 self
::setArrayTypeRecursive( $arr, $tag, $kvpKeyName );
757 /************************************************************************//**
763 * Test whether a key should be considered metadata
768 public static function isMetadataKey( $key ) {
769 return substr( $key, 0, 1 ) === '_';
773 * Apply transformations to an array, returning the transformed array.
775 * @see ApiResult::getResultData()
778 * @param array $transforms
779 * @return array|object
781 protected static function applyTransformations( array $dataIn, array $transforms ) {
782 $strip = isset( $transforms['Strip'] ) ?
$transforms['Strip'] : 'none';
783 if ( $strip === 'base' ) {
784 $transforms['Strip'] = 'none';
786 $transformTypes = isset( $transforms['Types'] ) ?
$transforms['Types'] : null;
787 if ( $transformTypes !== null && !is_array( $transformTypes ) ) {
788 throw new InvalidArgumentException( __METHOD__
. ':Value for "Types" must be an array' );
792 $data = self
::stripMetadataNonRecursive( $dataIn, $metadata );
794 if ( isset( $transforms['Custom'] ) ) {
795 if ( !is_callable( $transforms['Custom'] ) ) {
796 throw new InvalidArgumentException( __METHOD__
. ': Value for "Custom" must be callable' );
798 call_user_func_array( $transforms['Custom'], array( &$data, &$metadata ) );
801 if ( ( isset( $transforms['BC'] ) ||
$transformTypes !== null ) &&
802 isset( $metadata[self
::META_TYPE
] ) && $metadata[self
::META_TYPE
] === 'BCkvp' &&
803 !isset( $metadata[self
::META_KVP_KEY_NAME
] )
805 throw new UnexpectedValueException( 'Type "BCkvp" used without setting ' .
806 'ApiResult::META_KVP_KEY_NAME metadata item' );
809 // BC transformations
812 if ( isset( $transforms['BC'] ) ) {
813 if ( !is_array( $transforms['BC'] ) ) {
814 throw new InvalidArgumentException( __METHOD__
. ':Value for "BC" must be an array' );
816 if ( !in_array( 'nobool', $transforms['BC'], true ) ) {
817 $boolKeys = isset( $metadata[self
::META_BC_BOOLS
] )
818 ?
array_flip( $metadata[self
::META_BC_BOOLS
] )
822 if ( !in_array( 'no*', $transforms['BC'], true ) &&
823 isset( $metadata[self
::META_CONTENT
] ) && $metadata[self
::META_CONTENT
] !== '*'
825 $k = $metadata[self
::META_CONTENT
];
826 $data['*'] = $data[$k];
828 $metadata[self
::META_CONTENT
] = '*';
831 if ( !in_array( 'nosub', $transforms['BC'], true ) &&
832 isset( $metadata[self
::META_BC_SUBELEMENTS
] )
834 foreach ( $metadata[self
::META_BC_SUBELEMENTS
] as $k ) {
837 self
::META_CONTENT
=> '*',
838 self
::META_TYPE
=> 'assoc',
843 if ( isset( $metadata[self
::META_TYPE
] ) ) {
844 switch ( $metadata[self
::META_TYPE
] ) {
847 $metadata[self
::META_TYPE
] = 'default';
850 $transformTypes['ArmorKVP'] = $metadata[self
::META_KVP_KEY_NAME
];
856 // Figure out type, do recursive calls, and do boolean transform if necessary
857 $defaultType = 'array';
859 foreach ( $data as $k => &$v ) {
860 $v = is_array( $v ) ? self
::applyTransformations( $v, $transforms ) : $v;
861 if ( $boolKeys !== null && is_bool( $v ) && !isset( $boolKeys[$k] ) ) {
868 if ( is_string( $k ) ) {
869 $defaultType = 'assoc';
870 } elseif ( $k > $maxKey ) {
876 // Determine which metadata to keep
880 $keepMetadata = array();
883 $keepMetadata = &$metadata;
886 $keepMetadata = array_intersect_key( $metadata, array(
887 self
::META_INDEXED_TAG_NAME
=> 1,
888 self
::META_SUBELEMENTS
=> 1,
892 throw new InvalidArgumentException( __METHOD__
. ': Unknown value for "Strip"' );
895 // Type transformation
896 if ( $transformTypes !== null ) {
897 if ( $defaultType === 'array' && $maxKey !== count( $data ) - 1 ) {
898 $defaultType = 'assoc';
901 // Override type, if provided
902 $type = $defaultType;
903 if ( isset( $metadata[self
::META_TYPE
] ) && $metadata[self
::META_TYPE
] !== 'default' ) {
904 $type = $metadata[self
::META_TYPE
];
906 if ( ( $type === 'kvp' ||
$type === 'BCkvp' ) &&
907 empty( $transformTypes['ArmorKVP'] )
910 } elseif ( $type === 'BCarray' ) {
912 } elseif ( $type === 'BCassoc' ) {
916 // Apply transformation
919 $metadata[self
::META_TYPE
] = 'assoc';
920 $data +
= $keepMetadata;
921 return empty( $transformTypes['AssocAsObject'] ) ?
$data : (object)$data;
925 $data = array_values( $data );
926 $metadata[self
::META_TYPE
] = 'array';
927 return $data +
$keepMetadata;
931 $key = isset( $metadata[self
::META_KVP_KEY_NAME
] )
932 ?
$metadata[self
::META_KVP_KEY_NAME
]
933 : $transformTypes['ArmorKVP'];
934 $valKey = isset( $transforms['BC'] ) ?
'*' : 'value';
935 $assocAsObject = !empty( $transformTypes['AssocAsObject'] );
938 foreach ( $data as $k => $v ) {
943 if ( $strip === 'none' ) {
945 self
::META_PRESERVE_KEYS
=> array( $key ),
946 self
::META_CONTENT
=> $valKey,
947 self
::META_TYPE
=> 'assoc',
950 $ret[] = $assocAsObject ?
(object)$item : $item;
952 $metadata[self
::META_TYPE
] = 'array';
954 return $ret +
$keepMetadata;
957 throw new UnexpectedValueException( "Unknown type '$type'" );
960 return $data +
$keepMetadata;
965 * Recursively remove metadata keys from a data array or object
967 * Note this removes all potential metadata keys, not just the defined
971 * @param array|object $data
972 * @return array|object
974 public static function stripMetadata( $data ) {
975 if ( is_array( $data ) ||
is_object( $data ) ) {
976 $isObj = is_object( $data );
978 $data = (array)$data;
980 $preserveKeys = isset( $data[self
::META_PRESERVE_KEYS
] )
981 ?
(array)$data[self
::META_PRESERVE_KEYS
]
983 foreach ( $data as $k => $v ) {
984 if ( self
::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
986 } elseif ( is_array( $v ) ||
is_object( $v ) ) {
987 $data[$k] = self
::stripMetadata( $v );
991 $data = (object)$data;
998 * Remove metadata keys from a data array or object, non-recursive
1000 * Note this removes all potential metadata keys, not just the defined
1004 * @param array|object $data
1005 * @param array &$metadata Store metadata here, if provided
1006 * @return array|object
1008 public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
1009 if ( !is_array( $metadata ) ) {
1010 $metadata = array();
1012 if ( is_array( $data ) ||
is_object( $data ) ) {
1013 $isObj = is_object( $data );
1015 $data = (array)$data;
1017 $preserveKeys = isset( $data[self
::META_PRESERVE_KEYS
] )
1018 ?
(array)$data[self
::META_PRESERVE_KEYS
]
1020 foreach ( $data as $k => $v ) {
1021 if ( self
::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1027 $data = (object)$data;
1034 * Get the 'real' size of a result item. This means the strlen() of the item,
1035 * or the sum of the strlen()s of the elements if the item is an array.
1036 * @note Once the deprecated public self::size is removed, we can rename this back to a less awkward name.
1037 * @param mixed $value
1040 private static function valueSize( $value ) {
1042 if ( is_array( $value ) ||
1043 is_object( $value ) && !is_callable( array( $value, '__toString' ) )
1045 foreach ( $value as $k => $v ) {
1046 if ( !self
::isMetadataKey( $s ) ) {
1047 $s +
= self
::valueSize( $v );
1050 } elseif ( is_scalar( $value ) ) {
1051 $s = strlen( $value );
1058 * Return a reference to the internal data at $path
1060 * @param array|string|null $path
1061 * @param string $create
1062 * If 'append', append empty arrays.
1063 * If 'prepend', prepend empty arrays.
1064 * If 'dummy', return a dummy array.
1065 * Else, raise an error.
1068 private function &path( $path, $create = 'append' ) {
1069 $path = (array)$path;
1070 $ret = &$this->data
;
1071 foreach ( $path as $i => $k ) {
1072 if ( !isset( $ret[$k] ) ) {
1073 switch ( $create ) {
1078 $ret = array( $k => array() ) +
$ret;
1084 $fail = join( '.', array_slice( $path, 0, $i +
1 ) );
1085 throw new InvalidArgumentException( "Path $fail does not exist" );
1088 if ( !is_array( $ret[$k] ) ) {
1089 $fail = join( '.', array_slice( $path, 0, $i +
1 ) );
1090 throw new InvalidArgumentException( "Path $fail is not an array" );
1099 /************************************************************************//**
1105 * Call this function when special elements such as '_element'
1106 * are needed by the formatter, for example in XML printing.
1107 * @deprecated since 1.25, you shouldn't have been using it in the first place
1108 * @since 1.23 $flag parameter added
1109 * @param bool $flag Set the raw mode flag to this state
1111 public function setRawMode( $flag = true ) {
1112 // Can't wfDeprecated() here, since we need to set this flag from
1113 // ApiMain for BC with stuff using self::getIsRawMode as
1114 // "self::getIsXMLMode".
1115 $this->isRawMode
= $flag;
1119 * Returns true whether the formatter requested raw data.
1120 * @deprecated since 1.25, you shouldn't have been using it in the first place
1123 public function getIsRawMode() {
1124 /// @todo: After Wikibase stops calling this, warn
1125 return $this->isRawMode
;
1129 * Get the result's internal data array (read-only)
1130 * @deprecated since 1.25, use $this->getResultData() instead
1133 public function getData() {
1134 wfDeprecated( __METHOD__
, '1.25' );
1135 return $this->getResultData( null, array(
1138 'Strip' => $this->isRawMode ?
'bc' : 'all',
1143 * Disable size checking in addValue(). Don't use this unless you
1144 * REALLY know what you're doing. Values added while size checking
1145 * was disabled will not be counted (ever)
1146 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1148 public function disableSizeCheck() {
1149 wfDeprecated( __METHOD__
, '1.24' );
1150 $this->checkingSize
= false;
1154 * Re-enable size checking in addValue()
1155 * @deprecated since 1.24, use ApiResult::NO_SIZE_CHECK
1157 public function enableSizeCheck() {
1158 wfDeprecated( __METHOD__
, '1.24' );
1159 $this->checkingSize
= true;
1163 * Alias for self::setValue()
1165 * @since 1.21 int $flags replaced boolean $override
1166 * @deprecated since 1.25, use self::setValue() instead
1167 * @param array $arr To add $value to
1168 * @param string $name Index of $arr to add $value at
1169 * @param mixed $value
1170 * @param int $flags Zero or more OR-ed flags like OVERRIDE | ADD_ON_TOP.
1171 * This parameter used to be boolean, and the value of OVERRIDE=1 was
1172 * specifically chosen so that it would be backwards compatible with the
1173 * new method signature.
1175 public static function setElement( &$arr, $name, $value, $flags = 0 ) {
1176 wfDeprecated( __METHOD__
, '1.25' );
1177 return self
::setValue( $arr, $name, $value, $flags );
1181 * Adds a content element to an array.
1182 * Use this function instead of hardcoding the '*' element.
1183 * @deprecated since 1.25, use self::setContentValue() instead
1184 * @param array $arr To add the content element to
1185 * @param mixed $value
1186 * @param string $subElemName When present, content element is created
1187 * as a sub item of $arr. Use this parameter to create elements in
1188 * format "<elem>text</elem>" without attributes.
1190 public static function setContent( &$arr, $value, $subElemName = null ) {
1191 wfDeprecated( __METHOD__
, '1.25' );
1192 if ( is_array( $value ) ) {
1193 throw new InvalidArgumentException( __METHOD__
. ': Bad parameter' );
1195 if ( is_null( $subElemName ) ) {
1196 self
::setContentValue( $arr, 'content', $value );
1198 if ( !isset( $arr[$subElemName] ) ) {
1199 $arr[$subElemName] = array();
1201 self
::setContentValue( $arr[$subElemName], 'content', $value );
1206 * Set indexed tag name on all subarrays of $arr
1208 * Does not set the tag name for $arr itself.
1210 * @deprecated since 1.25, use self::setIndexedTagNameRecursive() instead
1212 * @param string $tag Tag name
1214 public function setIndexedTagName_recursive( &$arr, $tag ) {
1215 wfDeprecated( __METHOD__
, '1.25' );
1216 if ( !is_array( $arr ) ) {
1219 if ( !is_string( $tag ) ) {
1220 throw new InvalidArgumentException( 'Bad tag name' );
1222 foreach ( $arr as $k => &$v ) {
1223 if ( !self
::isMetadataKey( $k ) && is_array( $v ) ) {
1224 $v[self
::META_INDEXED_TAG_NAME
] = $tag;
1225 $this->setIndexedTagName_recursive( $v, $tag );
1231 * Alias for self::addIndexedTagName()
1232 * @deprecated since 1.25, use $this->addIndexedTagName() instead
1233 * @param array $path Path to the array, like addValue()'s $path
1234 * @param string $tag
1236 public function setIndexedTagName_internal( $path, $tag ) {
1237 wfDeprecated( __METHOD__
, '1.25' );
1238 $this->addIndexedTagName( $path, $tag );
1242 * Alias for self::addParsedLimit()
1243 * @deprecated since 1.25, use $this->addParsedLimit() instead
1244 * @param string $moduleName
1247 public function setParsedLimit( $moduleName, $limit ) {
1248 wfDeprecated( __METHOD__
, '1.25' );
1249 $this->addParsedLimit( $moduleName, $limit );
1253 * Set the ApiMain for use by $this->beginContinuation()
1255 * @deprecated for backwards compatibility only, do not use
1256 * @param ApiMain $main
1258 public function setMainForContinuation( ApiMain
$main ) {
1259 $this->mainForContinuation
= $main;
1263 * Parse a 'continue' parameter and return status information.
1265 * This must be balanced by a call to endContinuation().
1268 * @deprecated since 1.25, use ApiContinuationManager instead
1269 * @param string|null $continue
1270 * @param ApiBase[] $allModules
1271 * @param array $generatedModules
1274 public function beginContinuation(
1275 $continue, array $allModules = array(), array $generatedModules = array()
1277 wfDeprecated( __METHOD__
, '1.25' );
1278 if ( $this->mainForContinuation
->getContinuationManager() ) {
1279 throw new UnexpectedValueException(
1280 __METHOD__
. ': Continuation already in progress from ' .
1281 $this->mainForContinuation
->getContinuationManager()->getSource()
1285 // Ugh. If $continue doesn't match that in the request, temporarily
1286 // replace the request when creating the ApiContinuationManager.
1287 if ( $continue === null ) {
1290 if ( $this->mainForContinuation
->getVal( 'continue', '' ) !== $continue ) {
1291 $oldCtx = $this->mainForContinuation
->getContext();
1292 $newCtx = new DerivativeContext( $oldCtx );
1293 $newCtx->setRequest( new DerivativeRequest(
1294 $oldCtx->getRequest(),
1295 array( 'continue' => $continue ) +
$oldCtx->getRequest()->getValues(),
1296 $oldCtx->getRequest()->wasPosted()
1298 $this->mainForContinuation
->setContext( $newCtx );
1299 $reset = new ScopedCallback(
1300 array( $this->mainForContinuation
, 'setContext' ),
1304 $manager = new ApiContinuationManager(
1305 $this->mainForContinuation
, $allModules, $generatedModules
1309 $this->mainForContinuation
->setContinuationManager( $manager );
1312 $manager->isGeneratorDone(),
1313 $manager->getRunModules(),
1319 * @deprecated since 1.25, use ApiContinuationManager instead
1320 * @param ApiBase $module
1321 * @param string $paramName
1322 * @param string|array $paramValue
1324 public function setContinueParam( ApiBase
$module, $paramName, $paramValue ) {
1325 wfDeprecated( __METHOD__
, '1.25' );
1326 if ( $this->mainForContinuation
->getContinuationManager() ) {
1327 $this->mainForContinuation
->getContinuationManager()->addContinueParam(
1328 $module, $paramName, $paramValue
1335 * @deprecated since 1.25, use ApiContinuationManager instead
1336 * @param ApiBase $module
1337 * @param string $paramName
1338 * @param string|array $paramValue
1340 public function setGeneratorContinueParam( ApiBase
$module, $paramName, $paramValue ) {
1341 wfDeprecated( __METHOD__
, '1.25' );
1342 if ( $this->mainForContinuation
->getContinuationManager() ) {
1343 $this->mainForContinuation
->getContinuationManager()->addGeneratorContinueParam(
1344 $module, $paramName, $paramValue
1350 * Close continuation, writing the data into the result
1352 * @deprecated since 1.25, use ApiContinuationManager instead
1353 * @param string $style 'standard' for the new style since 1.21, 'raw' for
1354 * the style used in 1.20 and earlier.
1356 public function endContinuation( $style = 'standard' ) {
1357 wfDeprecated( __METHOD__
, '1.25' );
1358 if ( !$this->mainForContinuation
->getContinuationManager() ) {
1362 if ( $style === 'raw' ) {
1363 $data = $this->mainForContinuation
->getContinuationManager()->getRawContinuation();
1365 $this->addValue( null, 'query-continue', $data, self
::ADD_ON_TOP | self
::NO_SIZE_CHECK
);
1368 $this->mainForContinuation
->getContinuationManager()->setContinuationIntoResult( $this );
1373 * No-op, this is now checked on insert.
1374 * @deprecated since 1.25
1376 public function cleanUpUTF8() {
1377 wfDeprecated( __METHOD__
, '1.25' );
1381 * Get the 'real' size of a result item. This means the strlen() of the item,
1382 * or the sum of the strlen()s of the elements if the item is an array.
1383 * @deprecated since 1.25, no external users known and there doesn't seem
1384 * to be any case for such use over just checking the return value from the
1386 * @param mixed $value
1389 public static function size( $value ) {
1390 wfDeprecated( __METHOD__
, '1.25' );
1391 return self
::valueSize( $value );
1395 * Converts a Status object to an array suitable for addValue
1396 * @deprecated since 1.25, use ApiErrorFormatter::arrayFromStatus()
1397 * @param Status $status
1398 * @param string $errorType
1401 public function convertStatusToArray( $status, $errorType = 'error' ) {
1402 wfDeprecated( __METHOD__
, '1.25' );
1403 return $this->errorFormatter
->arrayFromStatus( $status, $errorType );
1410 * For really cool vim folding this needs to be at the end:
1411 * vim: foldmarker=@{,@} foldmethod=marker