1

I am writing a script to be executed by a client which will generate an array of floats (which I understand in PHP are supposed to be the same as doubles) and perform a post request to a page which will take the data and compute the mean, returning that data in the response. My plan is to attach send the array in the message body in json format. I found a tutorial online for doing POST requests in PHP from which I have taken the function do_post_request() and I can't get around the following exception:

PHP Fatal error:  Uncaught exception 'Exception' with message 'Problem with http://localhost/average.php' in /home/yak/restclient.php:20
Stack trace:
#0 /home/yak/restclient.php(5): do_post_request('http://localhos...', '[1.5,1.5,1.5,1....', 'Content-Type=ap...')
#1 {main}
    thrown in /home/yak/restclient.php on line 20

The client code:

<?php
$array = array_fill(0,5,1.5);
$data = json_encode($array);
echo $data;
$response = do_post_request("http://localhost/average.php",$data,"Content-Type=application/json");
echo $response;

function do_post_request($url, $data, $optional_headers = null)
{
  $params = array('http' => array(
              'method' => 'POST',
              'content' => $data
            ));
  if ($optional_headers !== null) {
    $params['http']['header'] = $optional_headers;
  }
  $ctx = stream_context_create($params);
  $fp = @fopen($url, 'r', false, $ctx);
  if (!$fp) {
    throw new Exception("Problem with $url");
  }
  $response = @stream_get_contents($fp);
  if ($response === false) {
    throw new Exception("Problem reading data from $url);
  }
  return $response;
}
?>

Would anybody be able to shed some light on this error, or better yet, suggest a better way of doing what I'm trying to do here? I have also tried using the http_post_data() function, but even after trying some fixes found on SO, I have been unable to get it to work (PHP sees it as an undefined function).

This is the server code:

<?php
header("Content-Type:application/json");
if(!empty($_POST['data'])) {
#calculate mean of each array and send results
    $data = $_POST['data'];
    $array = json_decode($data);
    $mean = array_sum($array)/count($array);
    respond(200,"OK",$mean);
} else {
#invalid request
    respond(400,"Invalid Request",NULL);
}
function respond($status,$message,$data) {
    header("HTTP/1.1 $status $message");
    $response['status']=$status;
    $response['message']=$message;
    $response['data']=$data;
    $json_response=json_encode($response);
    echo $json_response;
}
?>

This will eventually be adapted to deal with several arrays sent in one message, but to get started I thought this would be quite simple - I was wrong.

UPDATE: Using Arpita's answer the request is now going through but the response indicates an invalid request, as if it is not actually doing a POST.

Yak Attack
  • 105
  • 2
  • 11

2 Answers2

1

Try using Curl instead

$feed = http://some.url/goes/here
$ch = curl_init($feed);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json'));
echo $result = curl_exec($ch);

Notice the curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/json')); replace 'Accept: application/json' with what ever your output format would be.

Arpita
  • 1,386
  • 1
  • 15
  • 35
1

Pretty much the same as the other answer, I would go with cURL and call this function like you're doing it already, but using : instead of = in the optional headers as an array:

Client side

<?php
// $url is a string with the URL to send the data to
// $data is an associative array containing each value you want to send through POST, for example: array( 'data', json_encode($array, TRUE) );
// $optional_headers is a normal array containing strings (key:value, using ":" instead of "="), for example: array( 'Content-Type: application/json' );

function do_post_request($url, $data, $optional_headers)
{
    $ch = curl_init( $url );

    curl_setopt( $ch, CURLOPT_POST, TRUE ); // People usually use count($data) or even just 1 instead of TRUE here, but according to the documentation, it should be TRUE (any number greater than 0 will evaluate to TRUE anyway, so it's your personal call to use one or another)
    curl_setopt( $ch, CURLOPT_POSTFIELDS, serialize($data) );
    curl_setopt( $ch, CURLOPT_RETURNTRANSFER, TRUE );
    curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, TRUE ); // Not harmful, will follow Location: header redirects if the server sends them
    curl_setopt( $ch, CURLOPT_HTTPHEADER, $optional_headers );

    return curl_exec( $ch );
}


$array = array_fill(0,5,1.5);
$data = json_encode($array);
echo $data;
$response = do_post_request("http://localhost/average.php", $data, array('Content-Type: application/x-www-form-urlencoded', 'Accept: application/json')); // Note the difference: Content-Type says what you are SENDING, Accept is what you will ACCEPT as an answer. You're sending an encoded JSON inside a usual FORM format, so your Content-Type will be an HTML FORM format instead of a JSON one (in other words: you're sending a STRING that represents the JSON inside a FORM ARRAY)
echo $response;

?>

To clarify the last point (the optional headers with Content-Type), when you say Content-Type: application/json you should be sending a JSON string, I mean, for example:

[1, 2, 5, 3, 4] (POST URL-encodes to: %5B1%2C+2%2C+5%2C+3%2C+4%5D)

But you're actually sending this (which is Content-Type: application/x-www-form-urlencoded):

data=[1, 2, 5, 3, 4] (POST URL-encodes to: data=%5B1%2C+2%2C+5%2C+3%2C+4%5D)

application/x-www-form-urlencoded accepts key-value pairs just like the ones in a GET URL.

Alejandro Iván
  • 3,969
  • 1
  • 21
  • 30
  • Hmm, I see why this should work, but my server is still reacting as if the POST request is empty. I have accepted the answer because it has gotten me around the exception, but can you see any reason why the server isn't responding the way I expect? – Yak Attack Oct 21 '15 at 22:56
  • I believe it could be the empty() function. Have you tried using isset()? – Alejandro Iván Oct 21 '15 at 23:43
  • Yeah, same behaviour - even tried just setting the data to a simple string. The server reacts the same whether I send the string or NULL as data. – Yak Attack Oct 21 '15 at 23:49
  • 1
    Got it working by setting Content Type : multipart/form-data – Yak Attack Oct 22 '15 at 01:30
  • Wow nice! That's kinda weird, since `multipart/form-data` is used when you add attachments to the POST (like attached files, look at this: http://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data). Glad you got it working! – Alejandro Iván Oct 22 '15 at 01:33
  • I believe I read somewhere that url-encoded requires pretty much exactly what it says - URL encoded strings. For an associative array, multipart-data works. Even more interesting, though I imagine not particularly safe, if I don't set the header at all the server still seems more than happy to accept the data without knowing the format (perhaps HTTP assumes multipart/form-data if no header is provided?) – Yak Attack Oct 22 '15 at 02:20