27

I'm building a mailchimp integration and they require a POST call with JSON code.

No, I'm using this code that actually works:

$data = wp_remote_post($url, array(
    'headers'   => array('Content-Type' => 'application/json; charset=utf-8'),
    'body'      => json_encode($array_with_parameters),
    'method'    => 'POST'
));

But, it returns a PHP warning

Warning: http_build_query(): Parameter 1 expected to be Array or Object. Incorrect value given in ../wp-includes/Requests/Transport/cURL.php on line 507

How to avoit it?

I've tried to just use the plain array in 'body' index but MailChimp returns a JSON parsing error.

2
  • 1
    Have you applied this patch to core? core.trac.wordpress.org/ticket/37700
    – Otto
    Commented Aug 26, 2016 at 12:45
  • Interesting. Is a commercial plugin, then must work on any WP installation. But since seems to be a WP bug, for me is ok. Many thanks!
    – a-coder
    Commented Aug 26, 2016 at 14:05

1 Answer 1

36

Try setting the data_format parameter in your request like so:

$data = wp_remote_post($url, array(
    'headers'     => array('Content-Type' => 'application/json; charset=utf-8'),
    'body'        => json_encode($array_with_parameters),
    'method'      => 'POST',
    'data_format' => 'body',
));

It looks like the format may be defaulting to query, in which case WordPress attempts to format the data using http_build_query, which is giving you issues since you're already formatting the body as a string. Here's the relevant check in wp-includes/class-http.php:

if (!empty($data)) {
    $data_format = $options['data_format'];

    if ($data_format === 'query') {
        $url = self::format_get($url, $data);
        $data = '';
    }
    elseif (!is_string($data)) {
        $data = http_build_query($data, null, '&');
    }
}

Since your error is coming from line 507 of wp-includes/Requests/Transport/cURL.php, we can see that this is the root call to http_build_query:

protected static function format_get($url, $data) {
    if (!empty($data)) {
        $url_parts = parse_url($url);
        if (empty($url_parts['query'])) {
            $query = $url_parts['query'] = '';
        }
        else {
            $query = $url_parts['query'];
        }

        $query .= '&' . http_build_query($data, null, '&');
        $query = trim($query, '&');

        if (empty($url_parts['query'])) {
            $url .= '?' . $query;
        }
        else {
            $url = str_replace($url_parts['query'], $query, $url);
        }
    }
    return $url;
}
2
  • Life saver! Perfect answer. Commented Apr 11, 2022 at 4:13
  • 1
    It's now in wp-includes/Requests/src/Requests.php and wp-includes/Requests/src/Transport/*.php files, but the logic is the same. Seems like the value body is just a convention, the code only checks for anything that's not query, but sticking with that value works and is the most future-proof.
    – Walf
    Commented May 10, 2023 at 4:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.