Chiwda
Chiwda

Reputation: 1344

SOAP XML request using PHP is not working - probably headers problem

I need to post to a client's URL (in case it matters the client is on a .Net platform) using SOAP and XML. The request needs to look something like this:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:XXX="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX" xmlns:XXX1="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"> <soap:Header/>    <soap:Body>
      <tem:SaveXXXStatus>
         <!--Optional:-->
         <tem:req>
            <!--Optional:-->
            <XXX:AWBNumber>69184678146</XXX:AWBNumber>
            <!--Optional:--> 
               … etc. …
            <!--Optional:-->
            <XXX:pincode></XXX:pincode>
         </tem:req>
         <!--Optional:-->
         <tem:profile>
            <!--Optional:-->
            <XXX1:Api_type>S</XXX1:Api_type>
            <!--Optional:-->
            <XXX1:Area></XXX1:Area>
            <!--Optional:-->
            <XXX1:LicenceKey>xxxxxxxxxxxxxxxxxxx</XXX1:LicenceKey>
            <!--Optional:-->
            <XXX1:LoginID>XXXYYY</XXX1:LoginID>
            <!--Optional:-->
            <XXX1:Version>1</XXX1:Version>
         </tem:profile>
      </tem:SaveXXXStatus>    </soap:Body> </soap:Envelope>

I am using the following code:

        $ch = curl_init();
        //var_dump($ch);

        curl_setopt($ch, CURLOPT_URL,"https://example.com?wsdl");
        curl_setopt($ch, CURLOPT_HTTPHEADER, array(
            'tem:"http://tempuri.org/"',
            'Content-Type: text/xml',
            'XXX:"http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX"'
            'XXX1:"http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"'
        ));
        //curl_setopt($ch, CURLOPT_USERPWD, "XXXYYY:xxxxxxxxxxxxx"); //Probably not needed
    curl_setopt($ch, CURLOPT_POST, 1);
        $strRequest = "";
        $strRequest .= "AWBNumber=69184678161";
… etc….
        $strRequest .= "&pincode=";
        $strRequest .= "&Api_type=S";
        $strRequest .= "&Area=";
        $strRequest .= "&LicenceKey=xxxxxxxx";
        $strRequest .= "&LoginID=XXXYYY";
        $strRequest .= "&Version=1";
        curl_setopt($ch, CURLOPT_POSTFIELDS,$strRequest);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $server_output = curl_exec($ch);
        var_dump($server_output);

This post shows that the header (envelope etc.) can simply be added to the POSTFIELDS string but I tried that and it didn't work. Besides it seems like such a hack!

Anyway, no combination is working - I am getting a zero length string as a result ($server_output). What is the right way to pass the headers and what else needs to be fixed here?

Upvotes: 0

Views: 383

Answers (2)

Marcel
Marcel

Reputation: 5119

As I mentioned in the comments already, it would be much easier using the build in PHP SoapClient class and objects as entities. Here 's a little example, how you could solve your issue.

The PHP Soap Client

PHP got its own build in SoapClient which works pretty fine. Have a look how to initialize the client for development.

try {
    $client = new \SoapClient('https://example.com?wsdl', [
        'cache_wsdl' )> WSDL_CACHE_NONE,
        'exceptions' => true,
        'trace' => true,
    ]);
} catch (\SoapFault $fault) {
    echo "<pre>";
    var_dump($fault->getMessage());
    echo "</pre>";

    if ($client) {
        echo "<pre>";
        var_dump($client->__getLastRequest(), $client->__getLastResponse());
        echo "</pre>";
    }
}

This is a simple init of the SoapClient class with development options. Setting the trace option to true enables the use of the clients internal functions __getLastRequest() and __getLastResponse(). So you can see, what the client has sent and what the response looks like, if there 's any. I 'm using this for checking the xml that the client has sent.

Simple entities as objects that can be used by soap

SOAP defines itself as complex and simple type definitions. You can see this, if you call the clients own __getTypes() function. There will be displayed a lot of structs and simple type definitions, which are stored in the given wsdl file or in xsd files mentioned in the wsdl file. With this informations we are able to build our own object. In this example I 'm using simple stdClass objects. In production manner, you should use computed own objects.

$req = new \stdClass();
$req->AWBNumber = new \SoapVar(
    69184678146, 
    XSD_INT, 
    null, 
    null, 
    'AWBNumber', 
    'http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX'
);
$encodedReq = new \SoapVar($req, SOAP_ENC_OBJECT, null, null, 'req', 'http://tempuri.org/');

$saveXXXStatus = new \stdClass();
$saveXXXStatus->req = $encodedReq;
$encodedSaveXXXStatus = new \SoapVar($saveXXXStatus, SOAP_ENC_OBJECT, null, null, 'SaveXXXStatus, 'http://tempuri.org/');

// send the content with the soap client
$result = $client->SaveXXXStatus($encodedSaveXXXStatus);

Please keep in mind, that this is a short example which is incomplete and will lead to a soap fault. But what I 've done here? The req node in your xml is an object. You 'll find the definition of this object in the above metioned __getTypes() function output. In this example I 've compiled this object as a stdClass with the property AWBNumber. The AWBNumber itself is a SoapVar object. We use a soap var because of the namespaces, which are used by the soap client. After defining a property we encode the req object as a soap object, which is also a SoapVar instance.

After all we call the webservice method SaveXXXStatus with the encoded parameter.

The Last Request

If you send this example, the last request should look like:

<ns1:envelope xmlns:ns1="http://www.w3.org/2003/05/soap-envelope" 
    xmlns:ns2="http://tempuri.org/"
    xmlns:ns3="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX">
    <ns1:body>
        <ns2:SaveXXXStatus>
            <ns2:req>
                <ns3:AWBNumber>69184678146</ns3:AWBNumber>
            </ns2:req>
        </ns2:SaveXXXStatus>
    </ns1:body>
</ns1:envelope>

As I said before, this is just an example. You have to code all the nodes as SoapVar objects and append it to parents and finally call the webservice method with the complete encoded data.

Simple as pie, hm?

Upvotes: 0

Saurabh Sharma
Saurabh Sharma

Reputation: 440

i also stuck in this few days ago... but tried this way and got results

$xml_post_string ='<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tem="http://tempuri.org/" xmlns:XXX="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.XXX" xmlns:XXX1="http://schemas.datacontract.org/2004/07/XXXAPI.Entities.Admin"> <soap:Header/>    <soap:Body>
  <tem:SaveXXXStatus>
     <!--Optional:-->
     <tem:req>
        <!--Optional:-->
        <XXX:AWBNumber>69184678146</XXX:AWBNumber>
        <!--Optional:--> 
           … etc. …
        <!--Optional:-->
        <XXX:pincode></XXX:pincode>
     </tem:req>
     <!--Optional:-->
     <tem:profile>
        <!--Optional:-->
        <XXX1:Api_type>S</XXX1:Api_type>
        <!--Optional:-->
        <XXX1:Area></XXX1:Area>
        <!--Optional:-->
        <XXX1:LicenceKey>xxxxxxxxxxxxxxxxxxx</XXX1:LicenceKey>
        <!--Optional:-->
        <XXX1:LoginID>XXXYYY</XXX1:LoginID>
        <!--Optional:-->
        <XXX1:Version>1</XXX1:Version>
     </tem:profile>
  </tem:SaveXXXStatus>    </soap:Body> </soap:Envelope>';

 $headers = array(
            "Content-type: text/xml;charset=\"utf-8\"",
            "Accept: text/xml",
            "Cache-Control: no-cache",
            "Pragma: no-cache",
            "SOAPAction: url",
            "Content-length: " . strlen($xml_post_string),
        );
$ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
        curl_setopt($ch, CURLOPT_URL, 'yoururl');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_TIMEOUT, 10);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml_post_string);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        $response = curl_exec($ch);
print_r($response);

Upvotes: 1

Related Questions