+ public function removePreserveKeysList( $path, $names ) {
+ $arr = &$this->path( $path );
+ self::unsetPreserveKeysList( $arr, $names );
+ }
+
+ /**
+ * Set the array data type
+ *
+ * @since 1.25
+ * @param array &$arr
+ * @param string $type See ApiResult::META_TYPE
+ * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+ */
+ public static function setArrayType( array &$arr, $type, $kvpKeyName = null ) {
+ if ( !in_array( $type, [
+ 'default', 'array', 'assoc', 'kvp', 'BCarray', 'BCassoc', 'BCkvp'
+ ], true ) ) {
+ throw new InvalidArgumentException( 'Bad type' );
+ }
+ $arr[self::META_TYPE] = $type;
+ if ( is_string( $kvpKeyName ) ) {
+ $arr[self::META_KVP_KEY_NAME] = $kvpKeyName;
+ }
+ }
+
+ /**
+ * Set the array data type for a path
+ * @since 1.25
+ * @param array|string|null $path See ApiResult::addValue()
+ * @param string $tag See ApiResult::META_TYPE
+ * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+ */
+ public function addArrayType( $path, $tag, $kvpKeyName = null ) {
+ $arr = &$this->path( $path );
+ self::setArrayType( $arr, $tag, $kvpKeyName );
+ }
+
+ /**
+ * Set the array data type recursively
+ * @since 1.25
+ * @param array &$arr
+ * @param string $type See ApiResult::META_TYPE
+ * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+ */
+ public static function setArrayTypeRecursive( array &$arr, $type, $kvpKeyName = null ) {
+ self::setArrayType( $arr, $type, $kvpKeyName );
+ foreach ( $arr as $k => &$v ) {
+ if ( !self::isMetadataKey( $k ) && is_array( $v ) ) {
+ self::setArrayTypeRecursive( $v, $type, $kvpKeyName );
+ }
+ }
+ }
+
+ /**
+ * Set the array data type for a path recursively
+ * @since 1.25
+ * @param array|string|null $path See ApiResult::addValue()
+ * @param string $tag See ApiResult::META_TYPE
+ * @param string $kvpKeyName See ApiResult::META_KVP_KEY_NAME
+ */
+ public function addArrayTypeRecursive( $path, $tag, $kvpKeyName = null ) {
+ $arr = &$this->path( $path );
+ self::setArrayTypeRecursive( $arr, $tag, $kvpKeyName );
+ }
+
+ /**@}*/
+
+ /************************************************************************//**
+ * @name Utility
+ * @{
+ */
+
+ /**
+ * Test whether a key should be considered metadata
+ *
+ * @param string $key
+ * @return bool
+ */
+ public static function isMetadataKey( $key ) {
+ return substr( $key, 0, 1 ) === '_';
+ }
+
+ /**
+ * Apply transformations to an array, returning the transformed array.
+ *
+ * @see ApiResult::getResultData()
+ * @since 1.25
+ * @param array $dataIn
+ * @param array $transforms
+ * @return array|object
+ */
+ protected static function applyTransformations( array $dataIn, array $transforms ) {
+ $strip = isset( $transforms['Strip'] ) ? $transforms['Strip'] : 'none';
+ if ( $strip === 'base' ) {
+ $transforms['Strip'] = 'none';
+ }
+ $transformTypes = isset( $transforms['Types'] ) ? $transforms['Types'] : null;
+ if ( $transformTypes !== null && !is_array( $transformTypes ) ) {
+ throw new InvalidArgumentException( __METHOD__ . ':Value for "Types" must be an array' );
+ }
+
+ $metadata = [];
+ $data = self::stripMetadataNonRecursive( $dataIn, $metadata );
+
+ if ( isset( $transforms['Custom'] ) ) {
+ if ( !is_callable( $transforms['Custom'] ) ) {
+ throw new InvalidArgumentException( __METHOD__ . ': Value for "Custom" must be callable' );