PHP - POSTing XML data using cURL-less http web requests to a gateway

prince picture prince · Jun 2, 2014 · Viewed 7.1k times · Source

I am trying to find a cURL-less way to POST data via http web request to a 3rd party payment gateway. I have developed the following code which in actual fact communicates successfully with the receiving page but apparently the POST data is not being sent.

<?php
function makeWebRequest()
{
    //URL where to send the data via POST
    $url = 'http://localhost/connect_to_gateway/receive.php';

    //the actual data
    $xml = '<?xml version="1.0" encoding="UTF-8"?>' .
            '<test>' .
                'Hi there!' .
            '</test>';
    $data = array('xml' => $xml);

    //prepare the HTTP Headers
    $content_type = 'text/xml';
    $data = http_build_query($data);
    // use key 'http' even if you send the request to https://...
    $options = array(
        'http' => array(
            'method'  => 'POST',
            'header'  => 'Content-type: ' . addslashes($content_type) .'\r\n'
                            . 'Content-Length: ' . strlen($data) . '\r\n',
            'content' => $data,
        ),
    );
    $context  = stream_context_create($options);

    /*send the data using a cURL-less method*/
    $result = file_get_contents($url, false, $context);
    echo 'file_get_contents<br/>';
    var_dump($result); /*  <===  POST DATA NOT ARRIVING AT DESTINATION (I only receive a string with "START- FINISH - ") */
    echo '<br/><br/><br/><br/>';
}

//call the function
makeWebRequest(); 
?>

To examine why the data is not being received by the 3rd party page, I created my own receiving page (receive.php) and I am sending the request to my own script:

<?php

echo 'START- ';
if($_POST)
{
    echo 'INSIDE- ';
    echo htmlentities($_POST[0]);
}
echo 'FINISH -';
?>

I can confirm that it's true :(. The receiving script is not receiving the POST data because the response that I get from my own page is a string with the value: "START- FINISH- ". Had the POST data been received, the response text would have contained the word INSIDE- at the least.

Do you have any idea what am I doing wrong?

While I want specifically to set the content-type as 'text/xml' I also tried 'application/x-www-form-urlencoded' but still no success, $_POST is still empty. I also tried (a) omitting the Content-Length in the header, and (b) sending $xml directly instead of $data.

This tutorial on brugbart.com shows an example similair to mine. This forum post on sitepoint mentions something that I still do not understand i.e. that the header should be sent as a simple array instead of plain text if PHP is compiled with --with-curlwrappers (what does he mean there?). The way I interpreted the sitepoint comments was this but I still had no progress: $o

$options = array(
        'http' => array(
            'method'  => 'POST',
            'header'  => array(
                'Content-type' => addslashes($content_type),
                'Content-Length' => strlen($data)),
            'content' => $data,
        ),
    );

Any help would be much appreciated.

For those asking why I am avoiding cURL, it is because libcurl is not always installed on everyone's servers and I am trying to find a way to achieve the same thing without having to ask administrators to install libcurl if it was not installed in the original installation .. I am trying to use 'vanilla php'.

Other stackoverflow posts like these did not help:

Answer

prince picture prince · Jun 2, 2014

I found the solution to the troubles :)

I found out that when the Content-type is NOT application/x-www-form-urlencoded, $_POST in PHP will not have any data to read. Instead, one should access $HTTP_RAW_POST_DATA.

Therefore the code to SEND xml data via POST through file_get_contents is as follows:

<?php

function makeWebRequest()
{
    //URL where to send the data via POST
    $url = 'http://localhost/connect_to_gateway/receive.php';

    //the actual data
    $xml = '<?xml version="1.0" encoding="UTF-8"?>' .
            '<test>' .
                'Hi there!' .
            '</test>';

    //prepare the HTTP Headers
    $content_type = 'text/xml';
    // use key 'http' even if you send the request to https://...
    $options = array(
        'http' => array(
            'method'  => 'POST',
            'header'  => 'Content-type: ' . addslashes($content_type) .'\r\n'
                            . 'Content-Length: ' . strlen($xml) . '\r\n',
            'content' => $xml,
        ),
    );
    $context  = stream_context_create($options);

    /*send the data using a cURL-less method*/
    $result = file_get_contents($url, false, $context);
    echo 'file_get_contents<br/>';
    var_dump($result);
}

//call the function
makeWebRequest(); 
?>

The method to RECEIVE the data is as follows:

if ($HTTP_RAW_POST_DATA)
{   
    //create an xml parser and attempt to read it outputting errors if any
    $xml_parser=xml_parser_create();
    if(!xml_parse_into_struct($xml_parser, $HTTP_RAW_POST_DATA, $vals, $index))
        var_dump(array("ERROR"=>sprintf("XML error: %s at line %d",xml_error_string(xml_get_error_code($xml_parser)),xml_get_current_line_number($xml_parser))));
}

Thank you everyone .. phew.. that was painful!