2.2. More advanced interoperability demo using NuSoap: consume a .NET web service

2.2.1. Tip: a way to solve interoperability problem with a NuSoap client (for document/literal web service).

Your NuSoap client doesn't work and you try to consume a .NET document/literal web service? No stress, here is a tip to take full control of the SOAP message you send (because, obviously, we are going to write it "by hand", I love that! ;-) . In fact it is not a real workaround, but you will be able to send a pre-formatted SOAP message using some of the function defined in nusoap.php. Here we will use another web service (almost on-line 24/24) to test this case. We are going to consume a not-so-old version of a .NET web service: Infobel web service. You can use this service as a phone directory service demo. You have to send a login/password and some name and city and the service return a list of person matching your data. So we are going to search for Mr. Durand in Brussels, Belgium (Europe)...

NoteImportant note
 

The data returned by this web service are obsolete, are from a few years ago and can only be used for demo and testing purpose.

First a look at the wsdl, and more particularly at the Search() method (the only one ;-). This remote procedure take an object as param: inputQuery. Object composed by a list of field, and we will have to fill some of them, using sometime constant enumeration like Countries, Services...(See a wsdl excerpt below)

 
<s:schema elementFormDefault="qualified" targetNamespace="http://www.infobel.com/WebService/">
     <s:element name="Search">
        <s:complexType>
          <s:sequence>
            <s:element minOccurs="0" maxOccurs="1" name="inputQuery" type="s0:CQuery" />
          </s:sequence>
        </s:complexType>
      </s:element>
      <s:complexType name="CQuery">
        <s:sequence>
          <s:element minOccurs="0" maxOccurs="1" name="login" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="password" type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" name="country" type="s0:Countries" />
          <s:element minOccurs="1" maxOccurs="1" name="service" type="s0:Services" />
          <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="FirstName" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Street" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="bldnum" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="City" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Zip" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Phone" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Area" type="s:string" />
          <s:element minOccurs="0" maxOccurs="1" name="Category" type="s:string" />
          <s:element minOccurs="1" maxOccurs="1" name="XCoord" type="s:int" />
          <s:element minOccurs="1" maxOccurs="1" name="YCoord" type="s:int" />
          <s:element minOccurs="1" maxOccurs="1" name="Range" type="s:int" />
          <s:element minOccurs="1" maxOccurs="1" name="PageStep" type="s:int" />
          <s:element minOccurs="1" maxOccurs="1" name="Language" type="s0:Lang" />
          <s:element minOccurs="1" maxOccurs="1" name="CoordType" type="s0:CoordTypes" />
        </s:sequence>
      </s:complexType>
      <s:simpleType name="Countries">
        <s:restriction base="s:string">
          <s:enumeration value="aeCountryBE" />
          <s:enumeration value="aeCountryFR" />
          <s:enumeration value="aeCountryDK" />
          <s:enumeration value="aeCountryUK" />
          <s:enumeration value="aeCountryES" />
          <s:enumeration value="aeCountryNotAv" />
        </s:restriction>
      </s:simpleType>
      <s:simpleType name="Services">
        <s:restriction base="s:string">
          <s:enumeration value="aeSrvStandard" />
          <s:enumeration value="aeSrvExtended" />
          <s:enumeration value="aeSrvScore" />
        </s:restriction>
      </s:simpleType>
      <s:simpleType name="Lang">
        <s:restriction base="s:string">
          <s:enumeration value="aeLangFrench" />
          <s:enumeration value="aeLangDutch" />
          <s:enumeration value="aeLangGerman" />
          <s:enumeration value="aeLangEnglish" />
          <s:enumeration value="aeLangSpanish" />
        </s:restriction>
      </s:simpleType>
      <s:simpleType name="CoordTypes">
        <s:restriction base="s:string">
          <s:enumeration value="aeCTLamb2" />
          <s:enumeration value="aeCTWGS" />
        </s:restriction>
      </s:simpleType>

So let's try to compose by our-self the content of the body of our SOAP message to query this web service, we will use NuSoap to achieve the job:

<!-- we must use the targetnamespace this way,
  this .NET web service doesn't like the namespace prefix 
  generated automatically by NuSoap-->
<Search xmlns="http://www.infobel.com/WebService/">
<!-- nothing really difficult here, just
  follow the wsdl, or, use the tool TcpTunnelGui 
  with a working client, or see the documentation
  generated by the .NET web service on-line-->
  <inputQuery>
  <!-- we just have to specify the
  minOccurs="1" for our SOAP message to be accepted
  as a valid message by the web service. Add the other
  field, so the server will return some results... -->
    <login>infobel</login>
    <password>test</password>
    <country>aeCountryBE</country>
    <service>aeSrvStandard</service>
    <Name>durand</Name>
    <City>bruxelles</City>
    <Zip></Zip>
    <XCoord>0</XCoord>
    <YCoord>0</YCoord>
    <Range>0</Range>
    <PageStep>5</PageStep>
    <Language>aeLangFrench</Language>
    <CoordType>aeCTWGS</CoordType>
  </inputQuery>
</Search>

We plan here to use some function available in nusoap.php to make the call. Find the PHP code below to finalize our query:

Example 2-3. PHP code to query a web service using a pre-formated SOAP message

	require_once('nusoap.php');
        // define the soapaction as found in the wsdl
        $soapaction = "http://www.infobel.com/WebService/Search";
        // endpoint address
	$wsdl = "http://hal.kapitol.com/infobelservices/service1.asmx";
	$namespace = "http://www.infobel.com/WebService/";
	$client = new soapclient($wsdl);

        // you will find the serializeEnvelope() prototype by making a search in nusoap.php
	$mysoapmsg = $client->serializeEnvelope('<Search xmlns="http://www.infobel.com/WebService/"><inputQuery>
	<login>infobel</login>
	<password>test</password>
	<country>aeCountryBE</country>
	<service>aeSrvStandard</service>
	<Name>durand</Name>
	<City>bruxelles</City>
	<Zip></Zip>
	<XCoord>0</XCoord>
	<YCoord>0</YCoord>
	<Range>0</Range>
	<PageStep>5</PageStep>
	<Language>aeLangFrench</Language>
	<CoordType>aeCTWGS</CoordType>
	</inputQuery></Search>','',array(),'document', 'literal');

	/* Send the SOAP message and specify the soapaction  */

	$response = $client->send($mysoapmsg, $soapaction);

	if ($client->fault) {
          echo '<h3>Fault</h3><pre>';
          print_r($response);
          echo '</pre>';
        }

	echo "<p>SOAP query</p>";
	echo '<pre>' . htmlspecialchars($client->request, ENT_QUOTES) . '</pre>';
	echo "<p>SOAP response</p>";
	echo '<pre>' . htmlspecialchars($client->response, ENT_QUOTES) . '</pre>';

	
        // start to use the result by following the structure of the SOAP response
        // first extract the SearchResult
	$searchresult = $response["SearchResult"];
        // then you can access the Status message
	echo "Status message: ".$searchresult["Status"];
        // continue with the Result level
	$result = $searchresult["Result"];
        // wich allow access to NumRecs (number of record found)
	echo "<br>Record found: ".$result["NumRecs"];
        // extract the collection of record (object collRecord)
	$collrecord = $result["collRecord"];
        
	foreach ($collrecord["CRecord"] as $record){
		echo "<br>Name: ".$record["Name"];
		echo "<br>Address: ".$record["Address"];
                //...... and so on.....
	}

For a NuSoap client trying to access a .NET document/literal web service, we need to make a part of the job by hand. The interesting part here is for the use of the nusoap function serializeEnvelope.