MediaWiki REL1_35
ApiResult.php
Go to the documentation of this file.
1<?php
22
35class ApiResult implements ApiSerializable {
36
41 public const OVERRIDE = 1;
42
49 public const ADD_ON_TOP = 2;
50
58 public const NO_SIZE_CHECK = 4;
59
66 public const NO_VALIDATE = self::NO_SIZE_CHECK | 8;
67
72 public const META_INDEXED_TAG_NAME = '_element';
73
78 public const META_SUBELEMENTS = '_subelements';
79
84 public const META_PRESERVE_KEYS = '_preservekeys';
85
90 public const META_CONTENT = '_content';
91
110 public const META_TYPE = '_type';
111
119 public const META_KVP_KEY_NAME = '_kvpkeyname';
120
129 public const META_KVP_MERGE = '_kvpmerge';
130
136 public const META_BC_BOOLS = '_BC_bools';
137
143 public const META_BC_SUBELEMENTS = '_BC_subelements';
144
147
151 public function __construct( $maxSize ) {
152 $this->maxSize = $maxSize;
153 $this->reset();
154 }
155
161 public function setErrorFormatter( ApiErrorFormatter $formatter ) {
162 $this->errorFormatter = $formatter;
163 }
164
170 public function serializeForApiResult() {
171 return $this->data;
172 }
173
174 /************************************************************************/
182 public function reset() {
183 $this->data = [
184 self::META_TYPE => 'assoc', // Usually what's desired
185 ];
186 $this->size = 0;
187 }
188
242 public function getResultData( $path = [], $transforms = [] ) {
243 $path = (array)$path;
244 if ( !$path ) {
245 return self::applyTransformations( $this->data, $transforms );
246 }
247
248 $last = array_pop( $path );
249 $ret = &$this->path( $path, 'dummy' );
250 if ( !isset( $ret[$last] ) ) {
251 return null;
252 } elseif ( is_array( $ret[$last] ) ) {
253 return self::applyTransformations( $ret[$last], $transforms );
254 } else {
255 return $ret[$last];
256 }
257 }
258
263 public function getSize() {
264 return $this->size;
265 }
266
279 public static function setValue( array &$arr, $name, $value, $flags = 0 ) {
280 if ( ( $flags & self::NO_VALIDATE ) !== self::NO_VALIDATE ) {
281 $value = self::validateValue( $value );
282 }
283
284 if ( $name === null ) {
285 if ( $flags & self::ADD_ON_TOP ) {
286 array_unshift( $arr, $value );
287 } else {
288 array_push( $arr, $value );
289 }
290 return;
291 }
292
293 $exists = isset( $arr[$name] );
294 if ( !$exists || ( $flags & self::OVERRIDE ) ) {
295 if ( !$exists && ( $flags & self::ADD_ON_TOP ) ) {
296 $arr = [ $name => $value ] + $arr;
297 } else {
298 $arr[$name] = $value;
299 }
300 } elseif ( is_array( $arr[$name] ) && is_array( $value ) ) {
301 $conflicts = array_intersect_key( $arr[$name], $value );
302 if ( !$conflicts ) {
303 $arr[$name] += $value;
304 } else {
305 $keys = implode( ', ', array_keys( $conflicts ) );
306 throw new RuntimeException(
307 "Conflicting keys ($keys) when attempting to merge element $name"
308 );
309 }
310 } else {
311 throw new RuntimeException(
312 "Attempting to add element $name=$value, existing value is {$arr[$name]}"
313 );
314 }
315 }
316
322 private static function validateValue( $value ) {
323 if ( is_object( $value ) ) {
324 // Note we use is_callable() here instead of instanceof because
325 // ApiSerializable is an informal protocol (see docs there for details).
326 if ( is_callable( [ $value, 'serializeForApiResult' ] ) ) {
327 $oldValue = $value;
328 $value = $value->serializeForApiResult();
329 if ( is_object( $value ) ) {
330 throw new UnexpectedValueException(
331 get_class( $oldValue ) . '::serializeForApiResult() returned an object of class ' .
332 get_class( $value )
333 );
334 }
335
336 // Recursive call instead of fall-through so we can throw a
337 // better exception message.
338 try {
339 return self::validateValue( $value );
340 } catch ( Exception $ex ) {
341 throw new UnexpectedValueException(
342 get_class( $oldValue ) . '::serializeForApiResult() returned an invalid value: ' .
343 $ex->getMessage(),
344 0,
345 $ex
346 );
347 }
348 } elseif ( is_callable( [ $value, '__toString' ] ) ) {
349 $value = (string)$value;
350 } else {
351 $value = (array)$value + [ self::META_TYPE => 'assoc' ];
352 }
353 }
354 if ( is_array( $value ) ) {
355 // Work around https://bugs.php.net/bug.php?id=45959 by copying to a temporary
356 // (in this case, foreach gets $k === "1" but $tmp[$k] assigns as if $k === 1)
357 $tmp = [];
358 foreach ( $value as $k => $v ) {
359 $tmp[$k] = self::validateValue( $v );
360 }
361 $value = $tmp;
362 } elseif ( is_float( $value ) && !is_finite( $value ) ) {
363 throw new InvalidArgumentException( 'Cannot add non-finite floats to ApiResult' );
364 } elseif ( is_string( $value ) ) {
365 $value = MediaWikiServices::getInstance()->getContentLanguage()->normalize( $value );
366 } elseif ( $value !== null && !is_scalar( $value ) ) {
367 $type = gettype( $value );
368 // phpcs:ignore MediaWiki.Usage.ForbiddenFunctions.is_resource
369 if ( is_resource( $value ) ) {
370 $type .= '(' . get_resource_type( $value ) . ')';
371 }
372 throw new InvalidArgumentException( "Cannot add $type to ApiResult" );
373 }
374
375 return $value;
376 }
377
394 public function addValue( $path, $name, $value, $flags = 0 ) {
395 $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
396
397 if ( !( $flags & self::NO_SIZE_CHECK ) ) {
398 // self::size needs the validated value. Then flag
399 // to not re-validate later.
400 $value = self::validateValue( $value );
401 $flags |= self::NO_VALIDATE;
402
403 $newsize = $this->size + self::size( $value );
404 if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
405 $this->errorFormatter->addWarning(
406 'result', [ 'apiwarn-truncatedresult', Message::numParam( $this->maxSize ) ]
407 );
408 return false;
409 }
410 $this->size = $newsize;
411 }
412
413 self::setValue( $arr, $name, $value, $flags );
414 return true;
415 }
416
423 public static function unsetValue( array &$arr, $name ) {
424 $ret = null;
425 if ( isset( $arr[$name] ) ) {
426 $ret = $arr[$name];
427 unset( $arr[$name] );
428 }
429 return $ret;
430 }
431
442 public function removeValue( $path, $name, $flags = 0 ) {
443 $path = (array)$path;
444 if ( $name === null ) {
445 if ( !$path ) {
446 throw new InvalidArgumentException( 'Cannot remove the data root' );
447 }
448 $name = array_pop( $path );
449 }
450 $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
451 if ( !( $flags & self::NO_SIZE_CHECK ) ) {
452 $newsize = $this->size - self::size( $ret );
453 $this->size = max( $newsize, 0 );
454 }
455 return $ret;
456 }
457
467 public static function setContentValue( array &$arr, $name, $value, $flags = 0 ) {
468 if ( $name === null ) {
469 throw new InvalidArgumentException( 'Content value must be named' );
470 }
471 self::setContentField( $arr, $name, $flags );
472 self::setValue( $arr, $name, $value, $flags );
473 }
474
485 public function addContentValue( $path, $name, $value, $flags = 0 ) {
486 if ( $name === null ) {
487 throw new InvalidArgumentException( 'Content value must be named' );
488 }
489 $this->addContentField( $path, $name, $flags );
490 return $this->addValue( $path, $name, $value, $flags );
491 }
492
500 public function addParsedLimit( $moduleName, $limit ) {
501 // Add value, allowing overwriting
502 $this->addValue( 'limits', $moduleName, $limit,
503 self::OVERRIDE | self::NO_SIZE_CHECK );
504 }
505
508 /************************************************************************/
521 public static function setContentField( array &$arr, $name, $flags = 0 ) {
522 if ( isset( $arr[self::META_CONTENT] ) &&
523 isset( $arr[$arr[self::META_CONTENT]] ) &&
524 !( $flags & self::OVERRIDE )
525 ) {
526 throw new RuntimeException(
527 "Attempting to set content element as $name when " . $arr[self::META_CONTENT] .
528 ' is already set as the content element'
529 );
530 }
531 $arr[self::META_CONTENT] = $name;
532 }
533
542 public function addContentField( $path, $name, $flags = 0 ) {
543 $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
544 self::setContentField( $arr, $name, $flags );
545 }
546
554 public static function setSubelementsList( array &$arr, $names ) {
555 if ( !isset( $arr[self::META_SUBELEMENTS] ) ) {
556 $arr[self::META_SUBELEMENTS] = (array)$names;
557 } else {
558 $arr[self::META_SUBELEMENTS] = array_merge( $arr[self::META_SUBELEMENTS], (array)$names );
559 }
560 }
561
569 public function addSubelementsList( $path, $names ) {
570 $arr = &$this->path( $path );
571 self::setSubelementsList( $arr, $names );
572 }
573
581 public static function unsetSubelementsList( array &$arr, $names ) {
582 if ( isset( $arr[self::META_SUBELEMENTS] ) ) {
583 $arr[self::META_SUBELEMENTS] = array_diff( $arr[self::META_SUBELEMENTS], (array)$names );
584 }
585 }
586
594 public function removeSubelementsList( $path, $names ) {
595 $arr = &$this->path( $path );
596 self::unsetSubelementsList( $arr, $names );
597 }
598
605 public static function setIndexedTagName( array &$arr, $tag ) {
606 if ( !is_string( $tag ) ) {
607 throw new InvalidArgumentException( 'Bad tag name' );
608 }
609 $arr[self::META_INDEXED_TAG_NAME] = $tag;
610 }
611
618 public function addIndexedTagName( $path, $tag ) {
619 $arr = &$this->path( $path );
620 self::setIndexedTagName( $arr, $tag );
621 }
622
630 public static function setIndexedTagNameRecursive( array &$arr, $tag ) {
631 if ( !is_string( $tag ) ) {
632 throw new InvalidArgumentException( 'Bad tag name' );
633 }
634 $arr[self::META_INDEXED_TAG_NAME] = $tag;
635 foreach ( $arr as $k => &$v ) {
636 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
637 self::setIndexedTagNameRecursive( $v, $tag );
638 }
639 }
640 }
641
649 public function addIndexedTagNameRecursive( $path, $tag ) {
650 $arr = &$this->path( $path );
651 self::setIndexedTagNameRecursive( $arr, $tag );
652 }
653
664 public static function setPreserveKeysList( array &$arr, $names ) {
665 if ( !isset( $arr[self::META_PRESERVE_KEYS] ) ) {
666 $arr[self::META_PRESERVE_KEYS] = (array)$names;
667 } else {
668 $arr[self::META_PRESERVE_KEYS] = array_merge( $arr[self::META_PRESERVE_KEYS], (array)$names );
669 }
670 }
671
679 public function addPreserveKeysList( $path, $names ) {
680 $arr = &$this->path( $path );
681 self::setPreserveKeysList( $arr, $names );
682 }
683
691 public static function unsetPreserveKeysList( array &$arr, $names ) {
692 if ( isset( $arr[self::META_PRESERVE_KEYS] ) ) {
693 $arr[self::META_PRESERVE_KEYS] = array_diff( $arr[self::META_PRESERVE_KEYS], (array)$names );
694 }
695 }
696
704 public function removePreserveKeysList( $path, $names ) {
705 $arr = &$this->path( $path );
706 self::unsetPreserveKeysList( $arr, $names );
707 }
708
717 public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
718 if ( !in_array( $type, [
719 'default', 'array', 'assoc', 'kvp', 'BCarray', 'BCassoc', 'BCkvp'
720 ], true ) ) {
721 throw new InvalidArgumentException( 'Bad type' );
722 }
723 $arr[self::META_TYPE] = $type;
724 if ( is_string( $kvpKeyName ) ) {
725 $arr[self::META_KVP_KEY_NAME] = $kvpKeyName;
726 }
727 }
728
736 public function addArrayType( $path, $tag, $kvpKeyName = null ) {
737 $arr = &$this->path( $path );
738 self::setArrayType( $arr, $tag, $kvpKeyName );
739 }
740
748 public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
749 self::setArrayType( $arr, $type, $kvpKeyName );
750 foreach ( $arr as $k => &$v ) {
751 if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
752 self::setArrayTypeRecursive( $v, $type, $kvpKeyName );
753 }
754 }
755 }
756
764 public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
765 $arr = &$this->path( $path );
766 self::setArrayTypeRecursive( $arr, $tag, $kvpKeyName );
767 }
768
771 /************************************************************************/
782 public static function isMetadataKey( $key ) {
783 return substr( $key, 0, 1 ) === '_';
784 }
785
795 protected static function applyTransformations( array $dataIn, array $transforms ) {
796 $strip = $transforms['Strip'] ?? 'none';
797 if ( $strip === 'base' ) {
798 $transforms['Strip'] = 'none';
799 }
800 $transformTypes = $transforms['Types'] ?? null;
801 if ( $transformTypes !== null && !is_array( $transformTypes ) ) {
802 throw new InvalidArgumentException( __METHOD__ . ':Value for "Types" must be an array' );
803 }
804
805 $metadata = [];
806 $data = self::stripMetadataNonRecursive( $dataIn, $metadata );
807
808 if ( isset( $transforms['Custom'] ) ) {
809 if ( !is_callable( $transforms['Custom'] ) ) {
810 throw new InvalidArgumentException( __METHOD__ . ': Value for "Custom" must be callable' );
811 }
812 call_user_func_array( $transforms['Custom'], [ &$data, &$metadata ] );
813 }
814
815 if ( ( isset( $transforms['BC'] ) || $transformTypes !== null ) &&
816 isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] === 'BCkvp' &&
817 !isset( $metadata[self::META_KVP_KEY_NAME] )
818 ) {
819 throw new UnexpectedValueException( 'Type "BCkvp" used without setting ' .
820 'ApiResult::META_KVP_KEY_NAME metadata item' );
821 }
822
823 // BC transformations
824 $boolKeys = null;
825 if ( isset( $transforms['BC'] ) ) {
826 if ( !is_array( $transforms['BC'] ) ) {
827 throw new InvalidArgumentException( __METHOD__ . ':Value for "BC" must be an array' );
828 }
829 if ( !in_array( 'nobool', $transforms['BC'], true ) ) {
830 $boolKeys = isset( $metadata[self::META_BC_BOOLS] )
831 ? array_flip( $metadata[self::META_BC_BOOLS] )
832 : [];
833 }
834
835 if ( !in_array( 'no*', $transforms['BC'], true ) &&
836 isset( $metadata[self::META_CONTENT] ) && $metadata[self::META_CONTENT] !== '*'
837 ) {
838 $k = $metadata[self::META_CONTENT];
839 $data['*'] = $data[$k];
840 unset( $data[$k] );
841 $metadata[self::META_CONTENT] = '*';
842 }
843
844 if ( !in_array( 'nosub', $transforms['BC'], true ) &&
845 isset( $metadata[self::META_BC_SUBELEMENTS] )
846 ) {
847 foreach ( $metadata[self::META_BC_SUBELEMENTS] as $k ) {
848 if ( isset( $data[$k] ) ) {
849 $data[$k] = [
850 '*' => $data[$k],
851 self::META_CONTENT => '*',
852 self::META_TYPE => 'assoc',
853 ];
854 }
855 }
856 }
857
858 if ( isset( $metadata[self::META_TYPE] ) ) {
859 switch ( $metadata[self::META_TYPE] ) {
860 case 'BCarray':
861 case 'BCassoc':
862 $metadata[self::META_TYPE] = 'default';
863 break;
864 case 'BCkvp':
865 $transformTypes['ArmorKVP'] = $metadata[self::META_KVP_KEY_NAME];
866 break;
867 }
868 }
869 }
870
871 // Figure out type, do recursive calls, and do boolean transform if necessary
872 $defaultType = 'array';
873 $maxKey = -1;
874 foreach ( $data as $k => &$v ) {
875 $v = is_array( $v ) ? self::applyTransformations( $v, $transforms ) : $v;
876 if ( $boolKeys !== null && is_bool( $v ) && !isset( $boolKeys[$k] ) ) {
877 if ( !$v ) {
878 unset( $data[$k] );
879 continue;
880 }
881 $v = '';
882 }
883 if ( is_string( $k ) ) {
884 $defaultType = 'assoc';
885 } elseif ( $k > $maxKey ) {
886 $maxKey = $k;
887 }
888 }
889 unset( $v );
890
891 // Determine which metadata to keep
892 switch ( $strip ) {
893 case 'all':
894 case 'base':
895 $keepMetadata = [];
896 break;
897 case 'none':
898 $keepMetadata = &$metadata;
899 break;
900 case 'bc':
901 $keepMetadata = array_intersect_key( $metadata, [
902 self::META_INDEXED_TAG_NAME => 1,
903 self::META_SUBELEMENTS => 1,
904 ] );
905 break;
906 default:
907 throw new InvalidArgumentException( __METHOD__ . ': Unknown value for "Strip"' );
908 }
909
910 // Type transformation
911 if ( $transformTypes !== null ) {
912 if ( $defaultType === 'array' && $maxKey !== count( $data ) - 1 ) {
913 $defaultType = 'assoc';
914 }
915
916 // Override type, if provided
917 $type = $defaultType;
918 if ( isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] !== 'default' ) {
919 $type = $metadata[self::META_TYPE];
920 }
921 if ( ( $type === 'kvp' || $type === 'BCkvp' ) &&
922 empty( $transformTypes['ArmorKVP'] )
923 ) {
924 $type = 'assoc';
925 } elseif ( $type === 'BCarray' ) {
926 $type = 'array';
927 } elseif ( $type === 'BCassoc' ) {
928 $type = 'assoc';
929 }
930
931 // Apply transformation
932 switch ( $type ) {
933 case 'assoc':
934 $metadata[self::META_TYPE] = 'assoc';
935 $data += $keepMetadata;
936 return empty( $transformTypes['AssocAsObject'] ) ? $data : (object)$data;
937
938 case 'array':
939 ksort( $data );
940 $data = array_values( $data );
941 $metadata[self::META_TYPE] = 'array';
942 return $data + $keepMetadata;
943
944 case 'kvp':
945 case 'BCkvp':
946 $key = $metadata[self::META_KVP_KEY_NAME] ?? $transformTypes['ArmorKVP'];
947 $valKey = isset( $transforms['BC'] ) ? '*' : 'value';
948 $assocAsObject = !empty( $transformTypes['AssocAsObject'] );
949 $merge = !empty( $metadata[self::META_KVP_MERGE] );
950
951 $ret = [];
952 foreach ( $data as $k => $v ) {
953 if ( $merge && ( is_array( $v ) || is_object( $v ) ) ) {
954 $vArr = (array)$v;
955 if ( isset( $vArr[self::META_TYPE] ) ) {
956 $mergeType = $vArr[self::META_TYPE];
957 } elseif ( is_object( $v ) ) {
958 $mergeType = 'assoc';
959 } else {
960 $keys = array_keys( $vArr );
961 sort( $keys, SORT_NUMERIC );
962 $mergeType = ( $keys === array_keys( $keys ) ) ? 'array' : 'assoc';
963 }
964 } else {
965 $mergeType = 'n/a';
966 }
967 if ( $mergeType === 'assoc' ) {
968 $item = $vArr + [
969 $key => $k,
970 ];
971 if ( $strip === 'none' ) {
972 self::setPreserveKeysList( $item, [ $key ] );
973 }
974 } else {
975 $item = [
976 $key => $k,
977 $valKey => $v,
978 ];
979 if ( $strip === 'none' ) {
980 $item += [
981 self::META_PRESERVE_KEYS => [ $key ],
982 self::META_CONTENT => $valKey,
983 self::META_TYPE => 'assoc',
984 ];
985 }
986 }
987 $ret[] = $assocAsObject ? (object)$item : $item;
988 }
989 $metadata[self::META_TYPE] = 'array';
990
991 return $ret + $keepMetadata;
992
993 default:
994 throw new UnexpectedValueException( "Unknown type '$type'" );
995 }
996 } else {
997 return $data + $keepMetadata;
998 }
999 }
1000
1011 public static function stripMetadata( $data ) {
1012 if ( is_array( $data ) || is_object( $data ) ) {
1013 $isObj = is_object( $data );
1014 if ( $isObj ) {
1015 $data = (array)$data;
1016 }
1017 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1018 ? (array)$data[self::META_PRESERVE_KEYS]
1019 : [];
1020 foreach ( $data as $k => $v ) {
1021 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1022 unset( $data[$k] );
1023 } elseif ( is_array( $v ) || is_object( $v ) ) {
1024 $data[$k] = self::stripMetadata( $v );
1025 }
1026 }
1027 if ( $isObj ) {
1028 $data = (object)$data;
1029 }
1030 }
1031 return $data;
1032 }
1033
1045 public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
1046 if ( !is_array( $metadata ) ) {
1047 $metadata = [];
1048 }
1049 if ( is_array( $data ) || is_object( $data ) ) {
1050 $isObj = is_object( $data );
1051 if ( $isObj ) {
1052 $data = (array)$data;
1053 }
1054 $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1055 ? (array)$data[self::META_PRESERVE_KEYS]
1056 : [];
1057 foreach ( $data as $k => $v ) {
1058 if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1059 $metadata[$k] = $v;
1060 unset( $data[$k] );
1061 }
1062 }
1063 if ( $isObj ) {
1064 $data = (object)$data;
1065 }
1066 }
1067 return $data;
1068 }
1069
1076 private static function size( $value ) {
1077 $s = 0;
1078 if ( is_array( $value ) ) {
1079 foreach ( $value as $k => $v ) {
1080 if ( !self::isMetadataKey( $k ) ) {
1081 $s += self::size( $v );
1082 }
1083 }
1084 } elseif ( is_scalar( $value ) ) {
1085 $s = strlen( $value );
1086 }
1087
1088 return $s;
1089 }
1090
1102 private function &path( $path, $create = 'append' ) {
1103 $path = (array)$path;
1104 $ret = &$this->data;
1105 foreach ( $path as $i => $k ) {
1106 if ( !isset( $ret[$k] ) ) {
1107 switch ( $create ) {
1108 case 'append':
1109 $ret[$k] = [];
1110 break;
1111 case 'prepend':
1112 $ret = [ $k => [] ] + $ret;
1113 break;
1114 case 'dummy':
1115 $tmp = [];
1116 return $tmp;
1117 default:
1118 $fail = implode( '.', array_slice( $path, 0, $i + 1 ) );
1119 throw new InvalidArgumentException( "Path $fail does not exist" );
1120 }
1121 }
1122 if ( !is_array( $ret[$k] ) ) {
1123 $fail = implode( '.', array_slice( $path, 0, $i + 1 ) );
1124 throw new InvalidArgumentException( "Path $fail is not an array" );
1125 }
1126 $ret = &$ret[$k];
1127 }
1128 return $ret;
1129 }
1130
1139 public static function addMetadataToResultVars( $vars, $forceHash = true ) {
1140 // Process subarrays and determine if this is a JS [] or {}
1141 $hash = $forceHash;
1142 $maxKey = -1;
1143 $bools = [];
1144 foreach ( $vars as $k => $v ) {
1145 if ( is_array( $v ) || is_object( $v ) ) {
1146 $vars[$k] = self::addMetadataToResultVars( (array)$v, is_object( $v ) );
1147 } elseif ( is_bool( $v ) ) {
1148 // Better here to use real bools even in BC formats
1149 $bools[] = $k;
1150 }
1151 if ( is_string( $k ) ) {
1152 $hash = true;
1153 } elseif ( $k > $maxKey ) {
1154 $maxKey = $k;
1155 }
1156 }
1157 if ( !$hash && $maxKey !== count( $vars ) - 1 ) {
1158 $hash = true;
1159 }
1160
1161 // Set metadata appropriately
1162 if ( $hash ) {
1163 // Get the list of keys we actually care about. Unfortunately, we can't support
1164 // certain keys that conflict with ApiResult metadata.
1165 $keys = array_diff( array_keys( $vars ), [
1166 self::META_TYPE, self::META_PRESERVE_KEYS, self::META_KVP_KEY_NAME,
1167 self::META_INDEXED_TAG_NAME, self::META_BC_BOOLS
1168 ] );
1169
1170 return [
1171 self::META_TYPE => 'kvp',
1172 self::META_KVP_KEY_NAME => 'key',
1173 self::META_PRESERVE_KEYS => $keys,
1174 self::META_BC_BOOLS => $bools,
1175 self::META_INDEXED_TAG_NAME => 'var',
1176 ] + $vars;
1177 } else {
1178 return [
1179 self::META_TYPE => 'array',
1180 self::META_BC_BOOLS => $bools,
1181 self::META_INDEXED_TAG_NAME => 'value',
1182 ] + $vars;
1183 }
1184 }
1185
1194 public static function formatExpiry( $expiry, $infinity = 'infinity' ) {
1195 static $dbInfinity;
1196 if ( $dbInfinity === null ) {
1197 $dbInfinity = wfGetDB( DB_REPLICA )->getInfinity();
1198 }
1199
1200 if ( $expiry === '' || $expiry === null || $expiry === false ||
1201 wfIsInfinity( $expiry ) || $expiry === $dbInfinity
1202 ) {
1203 return $infinity;
1204 } else {
1205 return wfTimestamp( TS_ISO_8601, $expiry );
1206 }
1207 }
1208
1211}
1212
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
wfIsInfinity( $str)
Determine input string is represents as infinity.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Formats errors and warnings for the API, and add them to the associated ApiResult.
This class represents the result of the API operations.
Definition ApiResult.php:35
addArrayType( $path, $tag, $kvpKeyName=null)
Set the array data type for a path.
static unsetSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as attributes (when possible) rather than a...
static unsetPreserveKeysList(array &$arr, $names)
Don't preserve specified keys.
static applyTransformations(array $dataIn, array $transforms)
Apply transformations to an array, returning the transformed array.
const META_TYPE
Key for the 'type' metadata item.
static stripMetadataNonRecursive( $data, &$metadata=null)
Remove metadata keys from a data array or object, non-recursive.
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
serializeForApiResult()
Allow for adding one ApiResult into another.
static addMetadataToResultVars( $vars, $forceHash=true)
Add the correct metadata to an array of vars we want to export through the API.
static setValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name.
static validateValue( $value)
Validate a value for addition to the result.
const META_SUBELEMENTS
Key for the 'subelements' metadata item.
Definition ApiResult.php:78
addIndexedTagName( $path, $tag)
Set the tag name for numeric-keyed values in XML format.
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
const META_BC_BOOLS
Key for the 'BC bools' metadata item.
const META_PRESERVE_KEYS
Key for the 'preserve keys' metadata item.
Definition ApiResult.php:84
const NO_SIZE_CHECK
For addValue() and similar functions, do not check size while adding a value Don't use this unless yo...
Definition ApiResult.php:58
addArrayTypeRecursive( $path, $tag, $kvpKeyName=null)
Set the array data type for a path recursively.
getSize()
Get the size of the result, i.e.
__construct( $maxSize)
static unsetValue(array &$arr, $name)
Remove an output value to the array by name.
static setPreserveKeysList(array &$arr, $names)
Preserve specified keys.
addPreserveKeysList( $path, $names)
Preserve specified keys.
addParsedLimit( $moduleName, $limit)
Add the numeric limit for a limit=max to the result.
addSubelementsList( $path, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
static stripMetadata( $data)
Recursively remove metadata keys from a data array or object.
const META_CONTENT
Key for the 'content' metadata item.
Definition ApiResult.php:90
addIndexedTagNameRecursive( $path, $tag)
Set indexed tag name on $path and all subarrays.
removeSubelementsList( $path, $names)
Causes the elements with the specified names to be output as attributes (when possible) rather than a...
setErrorFormatter(ApiErrorFormatter $formatter)
Set the error formatter.
const OVERRIDE
Override existing value in addValue(), setValue(), and similar functions.
Definition ApiResult.php:41
& path( $path, $create='append')
Return a reference to the internal data at $path.
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
static setArrayTypeRecursive(array &$arr, $type, $kvpKeyName=null)
Set the array data type recursively.
const META_KVP_KEY_NAME
Key for the metadata item whose value specifies the name used for the kvp key in the alternative outp...
removeValue( $path, $name, $flags=0)
Remove value from the output data at the given path.
const ADD_ON_TOP
For addValue(), setValue() and similar functions, if the value does not exist, add it as the first el...
Definition ApiResult.php:49
getResultData( $path=[], $transforms=[])
Get the result data array.
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
removePreserveKeysList( $path, $names)
Don't preserve specified keys.
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
const META_INDEXED_TAG_NAME
Key for the 'indexed tag name' metadata item.
Definition ApiResult.php:72
const META_KVP_MERGE
Key for the metadata item that indicates that the KVP key should be added into an assoc value,...
reset()
Clear the current result data.
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
static setIndexedTagNameRecursive(array &$arr, $tag)
Set indexed tag name on $arr and all subarrays.
static setContentField(array &$arr, $name, $flags=0)
Set the name of the content field name (META_CONTENT)
static size( $value)
Get the 'real' size of a result item.
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
const NO_VALIDATE
For addValue(), setValue() and similar functions, do not validate data.
Definition ApiResult.php:66
addContentValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path and mark as META_CONTENT.
addContentField( $path, $name, $flags=0)
Set the name of the content field name (META_CONTENT)
static isMetadataKey( $key)
Test whether a key should be considered metadata.
MediaWikiServices is the service locator for the application scope of MediaWiki.
static numParam( $num)
Definition Message.php:1064
This interface allows for overriding the default conversion applied by ApiResult::validateValue().
const DB_REPLICA
Definition defines.php:25