0

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?

Chiwda
  • 914
  • 4
  • 25
  • 40
  • Why not using the build in PHP SoapClient class? It would be much easier working with objects as entities as sending strings via curl. – Marcel Jan 24 '19 at 09:41

2 Answers2

1

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);
Saurabh Sharma
  • 420
  • 2
  • 11
0

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?

Marcel
  • 3,617
  • 1
  • 10
  • 19
  • Not simple! Still trying to digest it. Is there a full/complete example somewhere or a tutorial? I have about 40 parameters to be passed - do I need to create each one as an object? Also, why have used only one schema URL? Do we not need a second or will it create errors? – Chiwda Jan 25 '19 at 06:38
  • There are generators like wsdl2php, which can provide the needed entities as php classes from the wsdl file. But this won 't change the way of proceeding soap with php. You will only need the URL of your wsdl file. All other namespaces are provided within the wsdl file. If you have to pass 40 parameters, you have to think about how to do it the easiest way. To keep it short: yes you have to pass every single member to the corresponding entity object as a soapvar instance, when working with soap and namespaces. – Marcel Jan 25 '19 at 08:01