MediaWiki  master
ApiResult.php
Go to the documentation of this file.
1 <?php
22 
35 class 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 
145  private $data, $size, $maxSize;
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  if ( is_resource( $value ) ) {
369  $type .= '(' . get_resource_type( $value ) . ')';
370  }
371  throw new InvalidArgumentException( "Cannot add $type to ApiResult" );
372  }
373 
374  return $value;
375  }
376 
393  public function addValue( $path, $name, $value, $flags = 0 ) {
394  $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
395 
396  if ( !( $flags & self::NO_SIZE_CHECK ) ) {
397  // self::size needs the validated value. Then flag
398  // to not re-validate later.
399  $value = self::validateValue( $value );
400  $flags |= self::NO_VALIDATE;
401 
402  $newsize = $this->size + self::size( $value );
403  if ( $this->maxSize !== false && $newsize > $this->maxSize ) {
404  $this->errorFormatter->addWarning(
405  'result', [ 'apiwarn-truncatedresult', Message::numParam( $this->maxSize ) ]
406  );
407  return false;
408  }
409  $this->size = $newsize;
410  }
411 
412  self::setValue( $arr, $name, $value, $flags );
413  return true;
414  }
415 
422  public static function unsetValue( array &$arr, $name ) {
423  $ret = null;
424  if ( isset( $arr[$name] ) ) {
425  $ret = $arr[$name];
426  unset( $arr[$name] );
427  }
428  return $ret;
429  }
430 
441  public function removeValue( $path, $name, $flags = 0 ) {
442  $path = (array)$path;
443  if ( $name === null ) {
444  if ( !$path ) {
445  throw new InvalidArgumentException( 'Cannot remove the data root' );
446  }
447  $name = array_pop( $path );
448  }
449  $ret = self::unsetValue( $this->path( $path, 'dummy' ), $name );
450  if ( !( $flags & self::NO_SIZE_CHECK ) ) {
451  $newsize = $this->size - self::size( $ret );
452  $this->size = max( $newsize, 0 );
453  }
454  return $ret;
455  }
456 
466  public static function setContentValue( array &$arr, $name, $value, $flags = 0 ) {
467  if ( $name === null ) {
468  throw new InvalidArgumentException( 'Content value must be named' );
469  }
470  self::setContentField( $arr, $name, $flags );
471  self::setValue( $arr, $name, $value, $flags );
472  }
473 
484  public function addContentValue( $path, $name, $value, $flags = 0 ) {
485  if ( $name === null ) {
486  throw new InvalidArgumentException( 'Content value must be named' );
487  }
488  $this->addContentField( $path, $name, $flags );
489  return $this->addValue( $path, $name, $value, $flags );
490  }
491 
499  public function addParsedLimit( $moduleName, $limit ) {
500  // Add value, allowing overwriting
501  $this->addValue( 'limits', $moduleName, $limit,
502  self::OVERRIDE | self::NO_SIZE_CHECK );
503  }
504 
507  /************************************************************************/
520  public static function setContentField( array &$arr, $name, $flags = 0 ) {
521  if ( isset( $arr[self::META_CONTENT] ) &&
522  isset( $arr[$arr[self::META_CONTENT]] ) &&
523  !( $flags & self::OVERRIDE )
524  ) {
525  throw new RuntimeException(
526  "Attempting to set content element as $name when " . $arr[self::META_CONTENT] .
527  ' is already set as the content element'
528  );
529  }
530  $arr[self::META_CONTENT] = $name;
531  }
532 
541  public function addContentField( $path, $name, $flags = 0 ) {
542  $arr = &$this->path( $path, ( $flags & self::ADD_ON_TOP ) ? 'prepend' : 'append' );
543  self::setContentField( $arr, $name, $flags );
544  }
545 
553  public static function setSubelementsList( array &$arr, $names ) {
554  if ( !isset( $arr[self::META_SUBELEMENTS] ) ) {
555  $arr[self::META_SUBELEMENTS] = (array)$names;
556  } else {
557  $arr[self::META_SUBELEMENTS] = array_merge( $arr[self::META_SUBELEMENTS], (array)$names );
558  }
559  }
560 
568  public function addSubelementsList( $path, $names ) {
569  $arr = &$this->path( $path );
570  self::setSubelementsList( $arr, $names );
571  }
572 
580  public static function unsetSubelementsList( array &$arr, $names ) {
581  if ( isset( $arr[self::META_SUBELEMENTS] ) ) {
582  $arr[self::META_SUBELEMENTS] = array_diff( $arr[self::META_SUBELEMENTS], (array)$names );
583  }
584  }
585 
593  public function removeSubelementsList( $path, $names ) {
594  $arr = &$this->path( $path );
595  self::unsetSubelementsList( $arr, $names );
596  }
597 
604  public static function setIndexedTagName( array &$arr, $tag ) {
605  if ( !is_string( $tag ) ) {
606  throw new InvalidArgumentException( 'Bad tag name' );
607  }
608  $arr[self::META_INDEXED_TAG_NAME] = $tag;
609  }
610 
617  public function addIndexedTagName( $path, $tag ) {
618  $arr = &$this->path( $path );
619  self::setIndexedTagName( $arr, $tag );
620  }
621 
629  public static function setIndexedTagNameRecursive( array &$arr, $tag ) {
630  if ( !is_string( $tag ) ) {
631  throw new InvalidArgumentException( 'Bad tag name' );
632  }
633  $arr[self::META_INDEXED_TAG_NAME] = $tag;
634  foreach ( $arr as $k => &$v ) {
635  if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
637  }
638  }
639  }
640 
648  public function addIndexedTagNameRecursive( $path, $tag ) {
649  $arr = &$this->path( $path );
650  self::setIndexedTagNameRecursive( $arr, $tag );
651  }
652 
663  public static function setPreserveKeysList( array &$arr, $names ) {
664  if ( !isset( $arr[self::META_PRESERVE_KEYS] ) ) {
665  $arr[self::META_PRESERVE_KEYS] = (array)$names;
666  } else {
667  $arr[self::META_PRESERVE_KEYS] = array_merge( $arr[self::META_PRESERVE_KEYS], (array)$names );
668  }
669  }
670 
678  public function addPreserveKeysList( $path, $names ) {
679  $arr = &$this->path( $path );
680  self::setPreserveKeysList( $arr, $names );
681  }
682 
690  public static function unsetPreserveKeysList( array &$arr, $names ) {
691  if ( isset( $arr[self::META_PRESERVE_KEYS] ) ) {
692  $arr[self::META_PRESERVE_KEYS] = array_diff( $arr[self::META_PRESERVE_KEYS], (array)$names );
693  }
694  }
695 
703  public function removePreserveKeysList( $path, $names ) {
704  $arr = &$this->path( $path );
705  self::unsetPreserveKeysList( $arr, $names );
706  }
707 
716  public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
717  if ( !in_array( $type, [
718  'default', 'array', 'assoc', 'kvp', 'BCarray', 'BCassoc', 'BCkvp'
719  ], true ) ) {
720  throw new InvalidArgumentException( 'Bad type' );
721  }
722  $arr[self::META_TYPE] = $type;
723  if ( is_string( $kvpKeyName ) ) {
724  $arr[self::META_KVP_KEY_NAME] = $kvpKeyName;
725  }
726  }
727 
735  public function addArrayType( $path, $tag, $kvpKeyName = null ) {
736  $arr = &$this->path( $path );
737  self::setArrayType( $arr, $tag, $kvpKeyName );
738  }
739 
747  public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
748  self::setArrayType( $arr, $type, $kvpKeyName );
749  foreach ( $arr as $k => &$v ) {
750  if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
751  self::setArrayTypeRecursive( $v, $type, $kvpKeyName );
752  }
753  }
754  }
755 
763  public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
764  $arr = &$this->path( $path );
765  self::setArrayTypeRecursive( $arr, $tag, $kvpKeyName );
766  }
767 
770  /************************************************************************/
781  public static function isMetadataKey( $key ) {
782  return substr( $key, 0, 1 ) === '_';
783  }
784 
794  protected static function applyTransformations( array $dataIn, array $transforms ) {
795  $strip = $transforms['Strip'] ?? 'none';
796  if ( $strip === 'base' ) {
797  $transforms['Strip'] = 'none';
798  }
799  $transformTypes = $transforms['Types'] ?? null;
800  if ( $transformTypes !== null && !is_array( $transformTypes ) ) {
801  throw new InvalidArgumentException( __METHOD__ . ':Value for "Types" must be an array' );
802  }
803 
804  $metadata = [];
805  $data = self::stripMetadataNonRecursive( $dataIn, $metadata );
806 
807  if ( isset( $transforms['Custom'] ) ) {
808  if ( !is_callable( $transforms['Custom'] ) ) {
809  throw new InvalidArgumentException( __METHOD__ . ': Value for "Custom" must be callable' );
810  }
811  call_user_func_array( $transforms['Custom'], [ &$data, &$metadata ] );
812  }
813 
814  if ( ( isset( $transforms['BC'] ) || $transformTypes !== null ) &&
815  isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] === 'BCkvp' &&
816  !isset( $metadata[self::META_KVP_KEY_NAME] )
817  ) {
818  throw new UnexpectedValueException( 'Type "BCkvp" used without setting ' .
819  'ApiResult::META_KVP_KEY_NAME metadata item' );
820  }
821 
822  // BC transformations
823  $boolKeys = null;
824  if ( isset( $transforms['BC'] ) ) {
825  if ( !is_array( $transforms['BC'] ) ) {
826  throw new InvalidArgumentException( __METHOD__ . ':Value for "BC" must be an array' );
827  }
828  if ( !in_array( 'nobool', $transforms['BC'], true ) ) {
829  $boolKeys = isset( $metadata[self::META_BC_BOOLS] )
830  ? array_flip( $metadata[self::META_BC_BOOLS] )
831  : [];
832  }
833 
834  if ( !in_array( 'no*', $transforms['BC'], true ) &&
835  isset( $metadata[self::META_CONTENT] ) && $metadata[self::META_CONTENT] !== '*'
836  ) {
837  $k = $metadata[self::META_CONTENT];
838  $data['*'] = $data[$k];
839  unset( $data[$k] );
840  $metadata[self::META_CONTENT] = '*';
841  }
842 
843  if ( !in_array( 'nosub', $transforms['BC'], true ) &&
844  isset( $metadata[self::META_BC_SUBELEMENTS] )
845  ) {
846  foreach ( $metadata[self::META_BC_SUBELEMENTS] as $k ) {
847  if ( isset( $data[$k] ) ) {
848  $data[$k] = [
849  '*' => $data[$k],
850  self::META_CONTENT => '*',
851  self::META_TYPE => 'assoc',
852  ];
853  }
854  }
855  }
856 
857  if ( isset( $metadata[self::META_TYPE] ) ) {
858  switch ( $metadata[self::META_TYPE] ) {
859  case 'BCarray':
860  case 'BCassoc':
861  $metadata[self::META_TYPE] = 'default';
862  break;
863  case 'BCkvp':
864  $transformTypes['ArmorKVP'] = $metadata[self::META_KVP_KEY_NAME];
865  break;
866  }
867  }
868  }
869 
870  // Figure out type, do recursive calls, and do boolean transform if necessary
871  $defaultType = 'array';
872  $maxKey = -1;
873  foreach ( $data as $k => &$v ) {
874  $v = is_array( $v ) ? self::applyTransformations( $v, $transforms ) : $v;
875  if ( $boolKeys !== null && is_bool( $v ) && !isset( $boolKeys[$k] ) ) {
876  if ( !$v ) {
877  unset( $data[$k] );
878  continue;
879  }
880  $v = '';
881  }
882  if ( is_string( $k ) ) {
883  $defaultType = 'assoc';
884  } elseif ( $k > $maxKey ) {
885  $maxKey = $k;
886  }
887  }
888  unset( $v );
889 
890  // Determine which metadata to keep
891  switch ( $strip ) {
892  case 'all':
893  case 'base':
894  $keepMetadata = [];
895  break;
896  case 'none':
897  $keepMetadata = &$metadata;
898  break;
899  case 'bc':
900  $keepMetadata = array_intersect_key( $metadata, [
901  self::META_INDEXED_TAG_NAME => 1,
902  self::META_SUBELEMENTS => 1,
903  ] );
904  break;
905  default:
906  throw new InvalidArgumentException( __METHOD__ . ': Unknown value for "Strip"' );
907  }
908 
909  // Type transformation
910  if ( $transformTypes !== null ) {
911  if ( $defaultType === 'array' && $maxKey !== count( $data ) - 1 ) {
912  $defaultType = 'assoc';
913  }
914 
915  // Override type, if provided
916  $type = $defaultType;
917  if ( isset( $metadata[self::META_TYPE] ) && $metadata[self::META_TYPE] !== 'default' ) {
918  $type = $metadata[self::META_TYPE];
919  }
920  if ( ( $type === 'kvp' || $type === 'BCkvp' ) &&
921  empty( $transformTypes['ArmorKVP'] )
922  ) {
923  $type = 'assoc';
924  } elseif ( $type === 'BCarray' ) {
925  $type = 'array';
926  } elseif ( $type === 'BCassoc' ) {
927  $type = 'assoc';
928  }
929 
930  // Apply transformation
931  switch ( $type ) {
932  case 'assoc':
933  $metadata[self::META_TYPE] = 'assoc';
934  $data += $keepMetadata;
935  return empty( $transformTypes['AssocAsObject'] ) ? $data : (object)$data;
936 
937  case 'array':
938  ksort( $data );
939  $data = array_values( $data );
940  $metadata[self::META_TYPE] = 'array';
941  return $data + $keepMetadata;
942 
943  case 'kvp':
944  case 'BCkvp':
945  $key = $metadata[self::META_KVP_KEY_NAME] ?? $transformTypes['ArmorKVP'];
946  $valKey = isset( $transforms['BC'] ) ? '*' : 'value';
947  $assocAsObject = !empty( $transformTypes['AssocAsObject'] );
948  $merge = !empty( $metadata[self::META_KVP_MERGE] );
949 
950  $ret = [];
951  foreach ( $data as $k => $v ) {
952  if ( $merge && ( is_array( $v ) || is_object( $v ) ) ) {
953  $vArr = (array)$v;
954  if ( isset( $vArr[self::META_TYPE] ) ) {
955  $mergeType = $vArr[self::META_TYPE];
956  } elseif ( is_object( $v ) ) {
957  $mergeType = 'assoc';
958  } else {
959  $keys = array_keys( $vArr );
960  sort( $keys, SORT_NUMERIC );
961  $mergeType = ( $keys === array_keys( $keys ) ) ? 'array' : 'assoc';
962  }
963  } else {
964  $mergeType = 'n/a';
965  }
966  if ( $mergeType === 'assoc' ) {
967  $item = $vArr + [
968  $key => $k,
969  ];
970  if ( $strip === 'none' ) {
971  self::setPreserveKeysList( $item, [ $key ] );
972  }
973  } else {
974  $item = [
975  $key => $k,
976  $valKey => $v,
977  ];
978  if ( $strip === 'none' ) {
979  $item += [
980  self::META_PRESERVE_KEYS => [ $key ],
981  self::META_CONTENT => $valKey,
982  self::META_TYPE => 'assoc',
983  ];
984  }
985  }
986  $ret[] = $assocAsObject ? (object)$item : $item;
987  }
988  $metadata[self::META_TYPE] = 'array';
989 
990  return $ret + $keepMetadata;
991 
992  default:
993  throw new UnexpectedValueException( "Unknown type '$type'" );
994  }
995  } else {
996  return $data + $keepMetadata;
997  }
998  }
999 
1010  public static function stripMetadata( $data ) {
1011  if ( is_array( $data ) || is_object( $data ) ) {
1012  $isObj = is_object( $data );
1013  if ( $isObj ) {
1014  $data = (array)$data;
1015  }
1016  $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1017  ? (array)$data[self::META_PRESERVE_KEYS]
1018  : [];
1019  foreach ( $data as $k => $v ) {
1020  if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1021  unset( $data[$k] );
1022  } elseif ( is_array( $v ) || is_object( $v ) ) {
1023  $data[$k] = self::stripMetadata( $v );
1024  }
1025  }
1026  if ( $isObj ) {
1027  $data = (object)$data;
1028  }
1029  }
1030  return $data;
1031  }
1032 
1044  public static function stripMetadataNonRecursive( $data, &$metadata = null ) {
1045  if ( !is_array( $metadata ) ) {
1046  $metadata = [];
1047  }
1048  if ( is_array( $data ) || is_object( $data ) ) {
1049  $isObj = is_object( $data );
1050  if ( $isObj ) {
1051  $data = (array)$data;
1052  }
1053  $preserveKeys = isset( $data[self::META_PRESERVE_KEYS] )
1054  ? (array)$data[self::META_PRESERVE_KEYS]
1055  : [];
1056  foreach ( $data as $k => $v ) {
1057  if ( self::isMetadataKey( $k ) && !in_array( $k, $preserveKeys, true ) ) {
1058  $metadata[$k] = $v;
1059  unset( $data[$k] );
1060  }
1061  }
1062  if ( $isObj ) {
1063  $data = (object)$data;
1064  }
1065  }
1066  return $data;
1067  }
1068 
1075  private static function size( $value ) {
1076  $s = 0;
1077  if ( is_array( $value ) ) {
1078  foreach ( $value as $k => $v ) {
1079  if ( !self::isMetadataKey( $k ) ) {
1080  $s += self::size( $v );
1081  }
1082  }
1083  } elseif ( is_scalar( $value ) ) {
1084  $s = strlen( $value );
1085  }
1086 
1087  return $s;
1088  }
1089 
1101  private function &path( $path, $create = 'append' ) {
1102  $path = (array)$path;
1103  $ret = &$this->data;
1104  foreach ( $path as $i => $k ) {
1105  if ( !isset( $ret[$k] ) ) {
1106  switch ( $create ) {
1107  case 'append':
1108  $ret[$k] = [];
1109  break;
1110  case 'prepend':
1111  $ret = [ $k => [] ] + $ret;
1112  break;
1113  case 'dummy':
1114  $tmp = [];
1115  return $tmp;
1116  default:
1117  $fail = implode( '.', array_slice( $path, 0, $i + 1 ) );
1118  throw new InvalidArgumentException( "Path $fail does not exist" );
1119  }
1120  }
1121  if ( !is_array( $ret[$k] ) ) {
1122  $fail = implode( '.', array_slice( $path, 0, $i + 1 ) );
1123  throw new InvalidArgumentException( "Path $fail is not an array" );
1124  }
1125  $ret = &$ret[$k];
1126  }
1127  return $ret;
1128  }
1129 
1138  public static function addMetadataToResultVars( $vars, $forceHash = true ) {
1139  // Process subarrays and determine if this is a JS [] or {}
1140  $hash = $forceHash;
1141  $maxKey = -1;
1142  $bools = [];
1143  foreach ( $vars as $k => $v ) {
1144  if ( is_array( $v ) || is_object( $v ) ) {
1145  $vars[$k] = self::addMetadataToResultVars( (array)$v, is_object( $v ) );
1146  } elseif ( is_bool( $v ) ) {
1147  // Better here to use real bools even in BC formats
1148  $bools[] = $k;
1149  }
1150  if ( is_string( $k ) ) {
1151  $hash = true;
1152  } elseif ( $k > $maxKey ) {
1153  $maxKey = $k;
1154  }
1155  }
1156  if ( !$hash && $maxKey !== count( $vars ) - 1 ) {
1157  $hash = true;
1158  }
1159 
1160  // Set metadata appropriately
1161  if ( $hash ) {
1162  // Get the list of keys we actually care about. Unfortunately, we can't support
1163  // certain keys that conflict with ApiResult metadata.
1164  $keys = array_diff( array_keys( $vars ), [
1165  self::META_TYPE, self::META_PRESERVE_KEYS, self::META_KVP_KEY_NAME,
1166  self::META_INDEXED_TAG_NAME, self::META_BC_BOOLS
1167  ] );
1168 
1169  return [
1170  self::META_TYPE => 'kvp',
1171  self::META_KVP_KEY_NAME => 'key',
1172  self::META_PRESERVE_KEYS => $keys,
1173  self::META_BC_BOOLS => $bools,
1174  self::META_INDEXED_TAG_NAME => 'var',
1175  ] + $vars;
1176  } else {
1177  return [
1178  self::META_TYPE => 'array',
1179  self::META_BC_BOOLS => $bools,
1180  self::META_INDEXED_TAG_NAME => 'value',
1181  ] + $vars;
1182  }
1183  }
1184 
1193  public static function formatExpiry( $expiry, $infinity = 'infinity' ) {
1194  static $dbInfinity;
1195  if ( $dbInfinity === null ) {
1196  $dbInfinity = wfGetDB( DB_REPLICA )->getInfinity();
1197  }
1198 
1199  if ( $expiry === '' || $expiry === null || $expiry === false ||
1200  wfIsInfinity( $expiry ) || $expiry === $dbInfinity
1201  ) {
1202  return $infinity;
1203  } else {
1204  return wfTimestamp( TS_ISO_8601, $expiry );
1205  }
1206  }
1207 
1210 }
1211 
ApiResult\addPreserveKeysList
addPreserveKeysList( $path, $names)
Preserve specified keys.
Definition: ApiResult.php:678
ApiResult\__construct
__construct( $maxSize)
Definition: ApiResult.php:151
Message\numParam
static numParam( $num)
Definition: Message.php:1035
ApiResult\addIndexedTagName
addIndexedTagName( $path, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:617
ApiSerializable
This interface allows for overriding the default conversion applied by ApiResult::validateValue().
Definition: ApiSerializable.php:37
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:160
ApiResult\setContentField
static setContentField(array &$arr, $name, $flags=0)
Set the name of the content field name (META_CONTENT)
Definition: ApiResult.php:520
ApiResult\META_TYPE
const META_TYPE
Key for the 'type' metadata item.
Definition: ApiResult.php:110
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1815
ApiResult\META_BC_SUBELEMENTS
const META_BC_SUBELEMENTS
Key for the 'BC subelements' metadata item.
Definition: ApiResult.php:143
ApiResult\setArrayTypeRecursive
static setArrayTypeRecursive(array &$arr, $type, $kvpKeyName=null)
Set the array data type recursively.
Definition: ApiResult.php:747
ApiResult\META_PRESERVE_KEYS
const META_PRESERVE_KEYS
Key for the 'preserve keys' metadata item.
Definition: ApiResult.php:84
ApiResult\$size
$size
Definition: ApiResult.php:145
ApiResult\addContentField
addContentField( $path, $name, $flags=0)
Set the name of the content field name (META_CONTENT)
Definition: ApiResult.php:541
ApiResult\applyTransformations
static applyTransformations(array $dataIn, array $transforms)
Apply transformations to an array, returning the transformed array.
Definition: ApiResult.php:794
ApiResult\setValue
static setValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name.
Definition: ApiResult.php:279
$s
$s
Definition: mergeMessageFileList.php:184
ApiResult\NO_SIZE_CHECK
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
ApiResult\META_KVP_KEY_NAME
const META_KVP_KEY_NAME
Key for the metadata item whose value specifies the name used for the kvp key in the alternative outp...
Definition: ApiResult.php:119
ApiResult\removeSubelementsList
removeSubelementsList( $path, $names)
Causes the elements with the specified names to be output as attributes (when possible) rather than a...
Definition: ApiResult.php:593
ApiResult\addArrayTypeRecursive
addArrayTypeRecursive( $path, $tag, $kvpKeyName=null)
Set the array data type for a path recursively.
Definition: ApiResult.php:763
ApiResult\NO_VALIDATE
const NO_VALIDATE
For addValue(), setValue() and similar functions, do not validate data.
Definition: ApiResult.php:66
ApiResult\serializeForApiResult
serializeForApiResult()
Allow for adding one ApiResult into another.
Definition: ApiResult.php:170
ApiResult\addContentValue
addContentValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path and mark as META_CONTENT.
Definition: ApiResult.php:484
ApiResult\path
& path( $path, $create='append')
Return a reference to the internal data at $path.
Definition: ApiResult.php:1101
ApiResult\getSize
getSize()
Get the size of the result, i.e.
Definition: ApiResult.php:263
ApiResult\addArrayType
addArrayType( $path, $tag, $kvpKeyName=null)
Set the array data type for a path.
Definition: ApiResult.php:735
ApiResult\setContentValue
static setContentValue(array &$arr, $name, $value, $flags=0)
Add an output value to the array by name and mark as META_CONTENT.
Definition: ApiResult.php:466
ApiResult\setArrayType
static setArrayType(array &$arr, $type, $kvpKeyName=null)
Set the array data type.
Definition: ApiResult.php:716
ApiResult\META_INDEXED_TAG_NAME
const META_INDEXED_TAG_NAME
Key for the 'indexed tag name' metadata item.
Definition: ApiResult.php:72
ApiResult\addParsedLimit
addParsedLimit( $moduleName, $limit)
Add the numeric limit for a limit=max to the result.
Definition: ApiResult.php:499
ApiResult\addMetadataToResultVars
static addMetadataToResultVars( $vars, $forceHash=true)
Add the correct metadata to an array of vars we want to export through the API.
Definition: ApiResult.php:1138
ApiResult
This class represents the result of the API operations.
Definition: ApiResult.php:35
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2475
ApiResult\META_SUBELEMENTS
const META_SUBELEMENTS
Key for the 'subelements' metadata item.
Definition: ApiResult.php:78
ApiResult\unsetValue
static unsetValue(array &$arr, $name)
Remove an output value to the array by name.
Definition: ApiResult.php:422
ApiResult\validateValue
static validateValue( $value)
Validate a value for addition to the result.
Definition: ApiResult.php:322
ApiResult\reset
reset()
Clear the current result data.
Definition: ApiResult.php:182
ApiResult\META_BC_BOOLS
const META_BC_BOOLS
Key for the 'BC bools' metadata item.
Definition: ApiResult.php:136
ApiResult\stripMetadataNonRecursive
static stripMetadataNonRecursive( $data, &$metadata=null)
Remove metadata keys from a data array or object, non-recursive.
Definition: ApiResult.php:1044
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
ApiResult\$errorFormatter
$errorFormatter
Definition: ApiResult.php:146
ApiResult\removeValue
removeValue( $path, $name, $flags=0)
Remove value from the output data at the given path.
Definition: ApiResult.php:441
ApiResult\addValue
addValue( $path, $name, $value, $flags=0)
Add value to the output data at the given path.
Definition: ApiResult.php:393
ApiResult\setErrorFormatter
setErrorFormatter(ApiErrorFormatter $formatter)
Set the error formatter.
Definition: ApiResult.php:161
ApiResult\unsetPreserveKeysList
static unsetPreserveKeysList(array &$arr, $names)
Don't preserve specified keys.
Definition: ApiResult.php:690
ApiResult\addSubelementsList
addSubelementsList( $path, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
Definition: ApiResult.php:568
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:604
ApiResult\size
static size( $value)
Get the 'real' size of a result item.
Definition: ApiResult.php:1075
wfIsInfinity
wfIsInfinity( $str)
Determine input string is represents as infinity.
Definition: GlobalFunctions.php:2793
ApiResult\removePreserveKeysList
removePreserveKeysList( $path, $names)
Don't preserve specified keys.
Definition: ApiResult.php:703
ApiResult\isMetadataKey
static isMetadataKey( $key)
Test whether a key should be considered metadata.
Definition: ApiResult.php:781
ApiErrorFormatter
Formats errors and warnings for the API, and add them to the associated ApiResult.
Definition: ApiErrorFormatter.php:31
ApiResult\$maxSize
$maxSize
Definition: ApiResult.php:145
ApiResult\setPreserveKeysList
static setPreserveKeysList(array &$arr, $names)
Preserve specified keys.
Definition: ApiResult.php:663
ApiResult\ADD_ON_TOP
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
ApiResult\getResultData
getResultData( $path=[], $transforms=[])
Get the result data array.
Definition: ApiResult.php:242
ApiResult\unsetSubelementsList
static unsetSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as attributes (when possible) rather than a...
Definition: ApiResult.php:580
$path
$path
Definition: NoLocalSettings.php:25
$keys
$keys
Definition: testCompression.php:72
ApiResult\OVERRIDE
const OVERRIDE
Override existing value in addValue(), setValue(), and similar functions.
Definition: ApiResult.php:41
ApiResult\formatExpiry
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
Definition: ApiResult.php:1193
ApiResult\META_KVP_MERGE
const META_KVP_MERGE
Key for the metadata item that indicates that the KVP key should be added into an assoc value,...
Definition: ApiResult.php:129
ApiResult\$data
$data
Definition: ApiResult.php:145
ApiResult\setIndexedTagNameRecursive
static setIndexedTagNameRecursive(array &$arr, $tag)
Set indexed tag name on $arr and all subarrays.
Definition: ApiResult.php:629
ApiResult\stripMetadata
static stripMetadata( $data)
Recursively remove metadata keys from a data array or object.
Definition: ApiResult.php:1010
ApiResult\META_CONTENT
const META_CONTENT
Key for the 'content' metadata item.
Definition: ApiResult.php:90
ApiResult\setSubelementsList
static setSubelementsList(array &$arr, $names)
Causes the elements with the specified names to be output as subelements rather than attributes.
Definition: ApiResult.php:553
ApiResult\addIndexedTagNameRecursive
addIndexedTagNameRecursive( $path, $tag)
Set indexed tag name on $path and all subarrays.
Definition: ApiResult.php:648
$type
$type
Definition: testCompression.php:52