3

I want to convert the following code from file_get_contents to curl. This is because file_get_contents doesn't return an error message from the API if it fails.

This is Microsoft's sample PHP script (slightly modified) which works as expected:

$key  = "my_valid_subscription_key_xxxxxxxxxxx";
$host = "https://api.cognitive.microsofttranslator.com";
$path = "/translate?api-version=3.0";

// Translate from English to Spanish.
$params = "&from=en&to=es";
$text   = "Hello world";

function create_guid()
{
    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand(0, 0xffff), mt_rand(0, 0xffff),
        mt_rand(0, 0xffff),
        mt_rand(0, 0x0fff) | 0x4000,
        mt_rand(0, 0x3fff) | 0x8000,
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
    );
}

function translate($host, $path, $key, $params, $content)
{
    $headers = "Content-type: application/json\r\n" .
    "Content-length: " . strlen($content) . "\r\n" .
    "Ocp-Apim-Subscription-Key: $key\r\n" .
    "X-ClientTraceId: " . create_guid() . "\r\n";

    $options = array(
        'http' => array(
            'header'  => $headers,
            'method'  => 'POST',
            'content' => $content,
        ),
    );
    $context = stream_context_create($options);
    $result  = file_get_contents($host . $path . $params, false, $context);
    return $result;
}

$request_body = [['Text' => $text]];
$content      = json_encode($request_body);
$result       = Translate($host, $path, $key, $params, $content);
$json         = json_decode($result);
echo $json[0]->{'translations'}[0]->{'text'}; // prints: Hola Mundo

This is my attempt to get it to work with curl. It's returning a code 401000 error message. The request is not authorized because credentials are missing or invalid. The get_token() function is working as expected.

$key  = "my_valid_subscription_key_xxxxxxxxxxx";    
$auth_url = "https://api.cognitive.microsoft.com/sts/v1.0/issueToken";

// Translate from English to Spanish.
$string    = "Hello world";
$from_lang = 'en';
$to_lang   = 'es';
$host      = "https://api.cognitive.microsofttranslator.com";
$path      = "/translate?api-version=3.0";
$host_path = $host . $path;

function get_token($key, $auth_url)
{
    $azure_key   = $key;
    $ch          = curl_init();
    $data_string = json_encode('{body}');
    $headers     = [
        'Content-Type: application/json',
        'Content-Length: ' . strlen($data_string),
        'Ocp-Apim-Subscription-Key: ' . $azure_key,
    ];
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_URL, $auth_url);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);
    return $response;
}

function create_guid()
{
    return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand(0, 0xffff), mt_rand(0, 0xffff),
        mt_rand(0, 0xffff),
        mt_rand(0, 0x0fff) | 0x4000,
        mt_rand(0, 0x3fff) | 0x8000,
        mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
    );
}

function translate($string, $from_lang, $to_lang, $host_path, $token, $guid)
{
    $headers = [
        "Authorization: Bearer " . $token . ", " .
        "Content-type: application/json, " .
        "Content-length: " . strlen($string) . ", " .
        "X-ClientTraceId: " . $guid
    ];

    $data = [
        'api-version' => '3.0',
        'body'        => urlencode($string),
        'from'        => 'en',
        'to'          => 'en',
        'textType'    => 'html',
    ];

    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $host_path);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
    curl_setopt($ch, CURLOPT_REFERER, $host_path);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_TIMEOUT, 15);
    $result = curl_exec($ch);
    $info   = curl_getinfo($ch);
    var_dump($info, $result);

}

$token = get_token($key, $auth_url); // this works as expected
$guid  = create_guid();
translate($string, $from_lang, $to_lang, $host_path, $token, $guid);

Here's the output from var_dump:

array (size=26)
  'url' => string 'https://api.cognitive.microsofttranslator.com/translate?api-version=3.0' (length=71)
  'content_type' => string 'application/json; charset=utf-8' (length=31)
  'http_code' => int 401
  'header_size' => int 308
  'request_size' => int 1239
  'filetime' => int -1
  'ssl_verify_result' => int 0
  'redirect_count' => int 0
  'total_time' => float 0.343
  'namelookup_time' => float 0.062
  'connect_time' => float 0.125
  'pretransfer_time' => float 0.265
  'size_upload' => float 62
  'size_download' => float 111
  'speed_download' => float 323
  'speed_upload' => float 180
  'download_content_length' => float 111
  'upload_content_length' => float 62
  'starttransfer_time' => float 0.343
  'redirect_time' => float 0
  'redirect_url' => string '' (length=0)
  'primary_ip' => string '40.90.141.99' (length=12)
  'certinfo' => 
    array (size=0)
      empty
  'primary_port' => int 443
  'local_ip' => string '192.168.0.103' (length=13)
  'local_port' => int 58143

string '{"error":{"code":401000,"message":"The request is not authorized because credentials are missing or invalid."}}' (length=111)
Leon
  • 1,478
  • 3
  • 16
  • 20
  • Possible duplicate of https://stackoverflow.com/questions/18513462/file-get-contents-get-full-response-even-on-error – 04FS Feb 25 '19 at 12:55
  • _“This is because file_get_contents doesn't return an error message from the API if it fails.”_ - you can easily make it do that, see mentioned duplicate … so no need to rewrite to cURL only for that. – 04FS Feb 25 '19 at 12:56
  • `$data_string = json_encode('{body}');` seems dubious and doesn't match the `file_get_contents` version. Also, in `translate` it looks from the original the API expects JSON, but that's not what you're sending in your replacement. – Jonnix Feb 25 '19 at 12:57
  • @04FS - Thanks. Adding ignore_errors => true to the http array for the stream_context_create options works well. With that sorted, I'll stick with file_get_contents. I'd still be interested to know why the curl version doesn't work. – Leon Feb 25 '19 at 13:26
  • @JonStirling - Doing it with curl, first I grab a token with `get_token()` - which works OK, then I send the token in `translate()` - which is what fails. The token is sent with the `Authorization: Bearer $token` header, and the `Content-type` header is being sent as `application/json`, so I think that's correct - unless I'm missing something that you're seeing? – Leon Feb 25 '19 at 13:34
  • Let's just put it this way. Your original example and your curl example are not making the same request, so don't get the same response. Since you're not sticking with the curl option anyway, and it would be somewhat time consuming to explain _and_ get it working, I'd suggest just leaving it at that. (Though if somebody else want to put the time in, that's great :P) – Jonnix Feb 25 '19 at 13:37
  • Microsoft's code is not issuing a separate request to get a `token`, so why are you trying to do that? also your code is trying to encode the POST data in `application/x-www-form-urlencoded`-format (using http_build_query()) while saying it is in `application/json`-format, that is obviously a bug. replace http_build_query() with json_encode(). – hanshenrik Feb 25 '19 at 19:28

0 Answers0