params = $params; $envelope->body = is_array( $body ) && [] === $body ? new \stdClass() : self::to_object( $body ); $envelope->path = $path; return $envelope; } /** Associative array -> stdClass (lists stay arrays). */ public static function to_object( mixed $value ): mixed { if ( $value instanceof \stdClass ) { foreach ( $value as $k => $v ) { $value->{$k} = self::to_object( $v ); } return $value; } if ( ! is_array( $value ) ) { return $value; } if ( self::is_list( $value ) ) { return array_map( [ self::class, 'to_object' ], $value ); } $object = new \stdClass(); foreach ( $value as $k => $v ) { $object->{(string) $k} = self::to_object( $v ); } return $object; } private static function sort_recursive( mixed $value ): mixed { if ( $value instanceof \stdClass ) { $pairs = (array) $value; ksort( $pairs, SORT_STRING ); $sorted = new \stdClass(); foreach ( $pairs as $k => $v ) { $sorted->{(string) $k} = self::sort_recursive( $v ); } return $sorted; } if ( ! is_array( $value ) ) { return $value; } // A list: order matters, keys are not sorted. if ( self::is_list( $value ) ) { return array_map( [ self::class, 'sort_recursive' ], $value ); } // An associative array is a JSON object. ksort( $value, SORT_STRING ); $sorted = new \stdClass(); foreach ( $value as $k => $v ) { $sorted->{(string) $k} = self::sort_recursive( $v ); } return $sorted; } private static function is_list( array $array ): bool { if ( [] === $array ) { return true; } return array_keys( $array ) === range( 0, count( $array ) - 1 ); } }