3

I need to recreate this in PHP:

curl -X POST --user <username>:<password>
--header "Content-Type: text/plain;charset=utf-8"
--header "Accept: application/json"
--data-binary @<filename>
"https://gateway.watsonplatform.net/personality-insights/api/v3/profile"

I've got this:

$request_headers = array();
$request_headers[] = 'Content-Type: text/plain;charset=utf-8';
$request_headers[] = 'Content-Language: en';
$request_headers[] = 'Accept-Language: en';

$simple_data = 'washingtonpost by the intelligence community';
    curl_setopt_array( $ch2, array(
        CURLOPT_POST => 1,
        CURLOPT_POSTFIELDS => $simple_data,
        CURLOPT_HEADER => $request_headers,
        CURLOPT_USERPWD => 'XXXX:YYYY',
    )
    );
    $response2 = curl_exec( $ch2 );

What my code doesn't account for is the --data-binary part, but I'm not sure how to "translate" this into PHP. In addition, can I use data binary with plain text (the API accepts it) instead of JSON?

jonmrich
  • 4,233
  • 5
  • 42
  • 94
  • 1
    In your code, `CURLOPT_HEADER` should be `CURLOPT_HTTPHEADER` to set request headers. Otherwise, POSTFIELDS is correct as is with the custom Content-Type. – drew010 Feb 21 '17 at 04:45
  • @drew010 This was exactly the problem! Thank you! If you submit as an answer, I can accept. Appreciate it. – jonmrich Feb 21 '17 at 12:19

3 Answers3

2

What you have is already a --data-binary equivalent. See the CURLOPT_POSTFIELDS API docs:

You must make sure that the data is formatted the way you want the server to receive it. libcurl will not convert or encode it for you in any way.

Compare that to the docs for the command-line --data-binary option:

This posts data exactly as specified with no extra processing whatsoever.

As far as the second part of your question:

can I use data binary with plain text (the API accepts it) instead of JSON

Yeah, both for --data-binary from the command line and CURLOPT_POSTFIELDS from the API.

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
  • Thanks...this is giving me `415 Unsupported Media Type` errors that I can't track down given the very simple string I'm trying to send in this example. – jonmrich Feb 21 '17 at 04:01
  • I would think that `415 Unsupported Media Type` is just the service looking at the `Content-Type: text/plain` header and saying it doesn’t support that (though why I dunno since your question indicates it should). I guess changing that to `application/json` and trying again would be the thing to do (if you’ve not already). But anyway as far as I can see, that 415 isn’t being caused by any malformedness in what your libcurl code is sending. – sideshowbarker Feb 21 '17 at 04:25
  • I'm running the exact same thing using the CLI and it works perfectly fine. Is there something that's being changed (or not changed) in the translation that I'm missing? – jonmrich Feb 21 '17 at 04:27
  • 2
    I think the problem is just what’s mentioned by in the comment above at http://stackoverflow.com/questions/42358104/translating-data-binary-from-curl-to-php/42358362?noredirect=1#comment71868188_42358104 That is, you need `CURLOPT_HTTPHEADER` instead of `CURLOPT_HEADER`. – sideshowbarker Feb 21 '17 at 05:16
0

If you set the value of CURLOPT_POSTFIELDS as an array (which if you use CURLFile it will be), the post will be formatted as multipart, breaking the data-binary part.

I did not find a way to use CURLFile wihtout the multipart/form-data side effect...

What I use is this, but it uses file_get_contents which is not very memory friendly (it will load the entire file in memory):

    <?php

    $file_local_full = '/tmp/foobar.png';

    $headers = array(
        "Content-Type: application/octet-stream", // or whatever you want
    );

    // if the file is too big, it should be streamed
    $curl_opts = array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_POSTFIELDS => file_get_contents($file_local_full),
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
    );
    // echo "curl_opts:\n" . print_r($curl_opts, true) . "\n"; exit;

    $curl = curl_init();
    curl_setopt_array($curl, $curl_opts);

    $response = curl_exec($curl);
0

Ok, I've found a way to stream the upload without multipart/form-data, the key is to cheat curl, first we tell him to PUT, then to POST:

    <?php
    
    $file_local_full = '/tmp/foobar.png';
    $content_type = mime_content_type($file_local_full);

    $headers = array(
        "Content-Type: $content_type", // or whatever you want
    );

    $filesize = filesize($file_local_full);
    $stream = fopen($file_local_full, 'r');

    $curl_opts = array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_PUT => true,
        CURLOPT_CUSTOMREQUEST => "POST",
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_INFILE => $stream,
        CURLOPT_INFILESIZE => $filesize,
        CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
    );

    $curl = curl_init();
    curl_setopt_array($curl, $curl_opts);

    $response = curl_exec($curl);

    fclose($stream);

    if (curl_errno($curl)) {
        $error_msg = curl_error($curl);
        throw new \Exception($error_msg);
    }

    curl_close($curl);

credits: How to POST a large amount of data within PHP curl without memory overhead?