17. Februar 2011

PHP : SOAP: Creating and Consuming a WSDL Web-Service

WebServices are very usefull if you need an common interface between external applications and your software. Especially WSDL driven WebServices are widely supported by many programming languages. Some SDKs offer possibilities to automatically create classes, functions and objects that you need to interact with the WSDL WebService.

The WSDL File

The WSDL File is an xml file that contains all information about the interface.
So we will find here information about the functionality the interface offers and how to access it. It also delivers the definition of parameters and return values required/delivered by the interface.

<?xml version ='1.0' encoding ='iso-8859-1' ?>
<wsdl:definitions
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
xmlns:tns="http://customer-ws.tld/customer-ws_Customers.wsdl"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="http://customer-ws.tld/customer-ws_Customers.wsdl"
name="customer-WS">



The definitions are requiered by the clients who access the WSDL-File to get information about the structure of the WSDL file and how to parse it.
targetNamespace and xmlns:tns are set to the URL (in my case: http://customer-ws.tld/customer-ws_Customers.wsdl) of the WSDL file.

Complex Types

<wsdl:types>
<s:schema
targetNamespace="http://customer-ws.tld/customer-ws_Customers.wsdl"
xmlns:tns="http://customer-ws.tld/PAF/customer-ws_Customers.wsdl">
<s:complexType name="Customer">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="customerNr" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="salutation" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="title" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="firstname" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="lastname" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="phone" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="fax" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="email" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="postalZip" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="postalCityName" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="postalStreet" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="postalStreetNumber" type="s:string"/>
</s:sequence>
</s:complexType>


<s:complexType name="CustomerArray">
<s:complexContent>
<s:restriction base="soapenc:Array">
<s:attribute ref="soapenc:arrayType" wsdl:arrayType="tns:Customer[]"/>
</s:restriction>
</s:complexContent>
</s:complexType>
</s:schema>
</wsdl:types>



The WSDL delivers simple data types like int, string etc. but in most cases we need much more complex data structures that have to be returned by our service. We have to define complex data types in the WSDL xml file. We define an object called 'Customer' and a type 'CustomerArray' which is an array consists of 'Customer' objects.

The Customer objects has the following attributes:

  • customerNr
  • salutation
  • title
  • firstname
  • lastname
  • phone
  • fax
  • email
  • postalZip
  • postalCityName
  • postalStreet
  • postalStreetNumber

Methods

Now it's time to define the methods our WebService should offer. For now we are creating 3 different methods:

1. getCustomers()
It returns an array of Customer objects.

2. getCustomer(aCustomerId)
It returns the customer with the given 'aCustomerId' as an Customer object

3. sendCustomer(aCustomer)
It sends a Customer Object to the server and returns the new Customer object.

<wsdl:message name='getCustomersRequest'>

</wsdl:message>
<wsdl:message name='getCustomersResponse'>
  <wsdl:part name='Result' type='tns:CustomerArray'/>
</wsdl:message>
<wsdl:message name='getCustomerRequest'>
  <wsdl:part name='aCustomerId' type='s:string'/>
</wsdl:message>
<wsdl:message name='getCustomerResponse'>
  <wsdl:part name='Result' type='tns:Customer'/>
</wsdl:message>
<wsdl:message name='sendCustomerRequest'>
  <wsdl:part name='aCustomer' type='tns:Customer'/>
</wsdl:message>
<wsdl:message name='sendCustomerResponse'>
  <wsdl:part name='Result' type='tns:Customer'/>
</wsdl:message>

<wsdl:portType name='CustomerPortType'>
  <wsdl:operation name='getCustomers'>
    <wsdl:input message='tns:getCustomersRequest'/>
    <wsdl:output message='tns:getCustomersResponse'/>
  </wsdl:operation>
  <wsdl:operation name='getCustomer'>
    <wsdl:input message='tns:getCustomerRequest'/>
    <wsdl:output message='tns:getCustomerResponse'/>
  </wsdl:operation>
  <wsdl:operation name='sendCustomer'>
    <wsdl:input message='tns:sendCustomerRequest'/>
    <wsdl:output message='tns:sendCustomerResponse'/>
  </wsdl:operation>  
</wsdl:portType>

<wsdl:binding name='ServiceSoap' type='tns:CustomerPortType'>
  <soap:binding style='rpc' transport='http://schemas.xmlsoap.org/soap/http'/>
  <wsdl:operation name='getCustomers'>
    <soap:operation soapAction='urn:customer-ws.tld-scramble#getCustomers'/>
    <wsdl:input>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:input>
    <wsdl:output>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:output>
  </wsdl:operation>
  <wsdl:operation name='getCustomer'>
    <soap:operation soapAction='urn:customer-ws.tld-scramble#getCustomer'/>
    <wsdl:input>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:input>
    <wsdl:output>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:output>
  </wsdl:operation>
  <wsdl:operation name='sendCustomer'>
    <soap:operation soapAction='urn:customer-ws.tld-scramble#sendCustomer'/>
    <wsdl:input>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:input>
    <wsdl:output>
      <soap:body use='encoded' namespace='urn:customer-ws.tld-scramble'
        encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'/>
    </wsdl:output>
  </wsdl:operation>    
</wsdl:binding>



 WSDL method definitions always consists of two parts:

  1. Request
  2. Response

The request defines the handle of the data send to the server and the response the resultset.

Service-Definition

The service definition describes how the WebService is accessed and where the server component is located.

<wsdl:service name='customer-WS'>
  <wsdl:port name="ServiceSoap" binding="tns:ServiceSoap">
    <soap:address location='http://customer-ws.tld/server.php'/>
  </wsdl:port>
</wsdl:service>
</wsdl:definitions>



The SOAP server

First of all we create a new PHP class that extends the SoapServer class delivered with PHP (if compiled in: for more details see here):
MySoapServer.class.php

<?php
 if(class_exists('SoapServer')){
class Customer_SoapServer extends SoapServer {

}
}
?>


Now we create the Customer class that we previously defined in the WSDL file:
Customer.class.php

<?php
class Customer {
public $customerNr;
public $salutation;
public $title;
public $firstname;
public $lastname;
public $phone;
public $fax;
public $email;
public $postalZip;
public $postalCityName;
public $postalStreet;
public $postalStreetNumber;
}
?>


Then we create the server script, that initializes the SOAP server and supplies the functions we have defined in the WSDL file:
server.php

<?php
include('Customer_SoapServer.class.php');
include('Customer.class.php');

public function getCustomers(){
$vCustomers = CustomerUtility::getAllCustomersArray();
$vReturnArray = array();

foreach($vCustomers as $vCustomerId => $vCustomer){
$vWSCustomer = getWSCustomer($vCustomer);

$vReturnArray[] = $vWSCustomer;
}

return $vReturnArray;
}

public function getWSCustomer($vCustomer){
$vWSCustomer = new Customer();
$vWSCustomer->customerNr = $vCustomer['id_user'];
$vWSCustomer->salutation = $vCustomer['salutation'];
$vWSCustomer->title = $vCustomer['title'];
$vWSCustomer->firstname = $vCustomer['firstname'];
$vWSCustomer->lastname = $vCustomer['lastname'];
$vWSCustomer->phone = $vCustomer['tel'];
$vWSCustomer->fax = $vCustomer['fax'];
$vWSCustomer->email = $vCustomer['email'];
$vWSCustomer->postalZip = $vCustomer['postal_zip'];
$vWSCustomer->postalCityName = $vCustomer['postal_city'];
$vWSCustomer->postalStreet = $vCustomer['postal_street'];
$vWSCustomer->postalStreetNumber = $vCustomer['postal_streetnumber'];
return $vWSCustomer;
}

public function getCustomer($aCustomerNr){
$vWSCustomer = getWSCustomer(CustomerUtility::getCustomerArrayByCustomerNr($aCustomerNr));

return $vWSCustomer;
}

public function sendCustomer($aWSCustomer){
$vCustomer = CustomerUtility::storeCustomer($aWSCustomer);

return getCustomer($aWSCustomer->customerNr);
}

// turn off the wsdl cache
ini_set("soap.wsdl_cache_enabled", "0");

$vServer = new Customer_SoapServer("customer-ws_Customers.wsdl");

$vServer->addFunction("getCustomers");
$vServer->addFunction("getCustomer");
$vServer->addFunction("sendCustomer");

$vServer->handle();
?>


I've left out the actions inside the CustomerUtility because anything can be done here: fetching/storing from/to database etc.

The SOAP client

Now we create the SOAP client script that fetches the data from the WSDL file and connects to the soap server and fetches the data.
client.php:

<?php
include('Customer.class.php');

// turn off the WSDL cache
ini_set('soap.wsdl_cache_enabled', '0');
$vLocation = 'http://customer-ws.tld/server.php';

try {
$vSoapClient = new SoapClient('http://customer-ws.tld/customer-ws_Customers.wsdl',
array(
"location" => $vLocation,
"trace"=>1, "exceptions"=>1
)
);

$vFunctions = $vSoapClient->__getFunctions();

echo "<pre>";
var_dump($vFunctions);
echo "</pre>";

$vCustomerObjectArray = $vSoapClient->__soapCall( 'getCustomers', array());

echo "<pre>";
var_dump($vCustomerObjectArray);
echo "</pre>";

$vSingleCustomerObject = $vCustomerObjectArray[0];

echo "<pre>";
var_dump($vSingleCustomerObject);
echo "</pre>";

$vSingleCustomerObject->lastname = "My-Changed-Lastname";

$vSendCustomerObject = $vSoapClient->__soapCall( 'sendCustomers', array($vSingleCustomerObject));

echo "<pre>";
var_dump($vSendCustomerObject);
echo "</pre>";
}catch (SoapFault $vException) {
echo "Error:<br />" . nl2br($vException->faultcode) . '<br /><br />Error Details:<br />'. nl2br($vException->faultstring) . '<br />';

echo("<br />REQUEST :<br />" . htmlspecialchars($vSoapClient->__getLastRequest()) . "<br />");
echo("<br />RESPONSE:<br />" .htmlspecialchars($vSoapClient->__getLastResponse()) . "<br />");
}
?>


After we connect to the server, we display all functions (__getFunctions) available by the SOAP webservice. Then we call the getCustomers function and the server responds with an array of customers and we take out the first customer, change its lastname to "My-Changed-Lastname" and send this object back to the server.

5 Kommentare:

  1. Hey
    PHP : SOAP: Creating and Consuming a WSDL Web-Service
    Your website is incredible, thank you in your effort!
    Do you have more great articles like this one
    Thank you!

    AntwortenLöschen
  2. HI
    PHP : SOAP: Creating and Consuming a WSDL Web-Service
    nice and useful! i would like to say thank you about this cool post & your Decent a site….
    thank you!

    AntwortenLöschen
  3. Hi,

    To ease the use of the SoapClient, take a look to projects like wsdl2php or WsdlToPhp like on http://www.wsdltophp.com.

    AntwortenLöschen
  4. The blog was absolutely fantastic! lot of great information which can be in some helpful or the other way. keep updating the blog, looking forward for more happy ... great job, keep it up ... PHP Website Services

    AntwortenLöschen