9. Using WSDL

In the WSDL mode, by providing a WSDL file, you can invoke a service or provide a service, without having to construct the payload manually. Few simple steps are required to configure either the client or the service to use the WSDL mode.WSO2 WSF/PHP supports both WSDL 1.1 and WSDL 2.0 in WSDL mode.

9.1. Writing a Simple Client to Use the WSDL Mode

There are two WSDL(WSDL 1.1 and WSDL 2.0) files provided in the samples\wsdl_mode folder and sample_wsdl_11.wsdl will be used for the example in this manual. Please have a look at the samples\wsdl_mode\sample_wsdl_11.wsdl.

1.create a WSClient object as follows 
 
$client = new WSClient(array("wsdl"=>"sample_wsdl_11.wsdl"));
       
 If the service endpoint is different from the location which is defined in the WSDL then you can specify it as 
 $client = new WSClient(array("wsdl"=>"sample_wsdl_11.wsdl",
			    "to" => "http://tempuri.org"));

2.obtain a WSClientProxy object from the WSClient as follows:  

$proxy = $client->getProxy();

Now you can call any operation defined in the WSDL. For example,you can invoke the QueryPurchaseOrder operation defined in the wsdl, as follows.

 $return_value = $proxy->QueryPurchaseOrder($input);

The arguments  for the function will be either an
    object defined in the class map or an associative array of arguments which can be constructed in the following two ways. First you should have understood the values
    that should be pass to the input as defined in the schema in WSDL. You should pass input and output parameters to the function in exactly the same order it is defined
    in schema. 
First we will have a look at the schema for the input message. 
   <xsd:element name="orderInfo">
		<xsd:complexType>
		    <xsd:sequence>
				<xsd:element name="productName" type="xsd:string"></xsd:element>
				<xsd:element name="quantity" type="xsd:float"></xsd:element>
				<xsd:element name="date" type="xsd:dateTime"></xsd:element>
				<xsd:element name="orderNo" type="xsd:int"></xsd:element>
			</xsd:sequence>
		</xsd:complexType>
  </xsd:element>

9.1.1.Associative array of arguments
As you can see, the input message element of the operation QueryPurchaseOrder 
    in WSDL , is OrderInfo which is a complex type defined in the schema. The complex type OrderInfo has four elements. So the array that you have to construct will be as follows.

$input_array =array("productName" => "Testing",
    		"quantity" => 234,
    		"date" => date(20031234),
   		 "orderNo" => 345));

Here the key of the array will be the element name of the complex type and the user should provide the appropriate value for the key. If the type of the element is another complex type, there should be another array instead of the value. 
As an example, imagine if the quantity element's type is another complex type defined in the schema as follows.
    
   
    <xsd:element name="orderInfo"> 
        <xsd:complexType>
           <xsd:sequence> 
               <xsd:element name="productName" type="xsd:string"></xsd:element>
               <xsd:element name="quantity" type="tns:quantityType"></xsd:element>
               <xsd:element name="date" type="xsd:dateTime"></xsd:element> 
               <xsd:element name="orderNo" type="xsd:int"></xsd:element> 
              </xsd:sequence> 
          </xsd:complexType>
        </xsd:element> 

        <xsd:element name="quantityType">
            <xsd:complexType> 
	<xsd:sequence> 
            	 <xsd:element name="amount" type="xsd:int"></xsd:element>
            	 <xsd:element name="weight_kg" type="xsd:float"></xsd:element> 
	</xsd:sequence>
            </xsd:complexType> 
         </xsd:element> 
            
 
Now the array that you should create will be, 

$input_array =array("productName" => "Testing",
		 "quantity" => $quantity_array, 
		"date" => date(20031234), 
		"orderNo" => 345)); 

$quantity_array = array("amount" => 2300, 
		    "weight_kg" => 203443); 

After creating the array, pass it to the operation that you are going to invoke. 
    
    $proxy->QueryPurchaseOrder($input_array); 

Since you have passed the array type arguments to the function, the return value will also be an associative array. The schema
    for the return message in the sample wsdl is as follows.
            	<xsd:element name="orderDetails">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="shipTo" type="tns:address"> </xsd:element>
				<xsd:element name="billTo" type="tns:address"></xsd:element>
				<xsd:element name="product" type="tns:orderInfo"></xsd:element>
			</xsd:sequence>
		    </xsd:complexType>
		</xsd:element>
			
        <xsd:complexType name="address">
            	<xsd:sequence>
            		<xsd:element name="name" type="xsd:string"></xsd:element>
            		<xsd:element name="street" type="xsd:string"></xsd:element>
            		<xsd:element name="city" type="xsd:string"></xsd:element>
            		<xsd:element name="state" type="xsd:string"></xsd:element>
            		<xsd:element name="zip" type="xsd:decimal"></xsd:element>
            	</xsd:sequence>
        </xsd:complexType>
        
         <xsd:complexType name="orderInfo">
            	<xsd:sequence>
            		<xsd:element name="productId" type="xsd:int"></xsd:element>
            		<xsd:element name="shippingDate" type="xsd:dateTime"></xsd:element>
            		<xsd:element name="status" type="xsd:boolean"></xsd:element>
            	</xsd:sequence>
         </xsd:complexType>
         
So the returned array will be of the same structure
    that we created for the input array. The returned array has three elements shipTo,
    billTo and product. Since all of these elements are complex types, there should be an associative array for the corresponding elements. Now the return array becomes,
   
$return_array = $proxy->QueryPurchaseOrder($input_array); 

$return_array = array("shipTo" => $shipToArray,
    		"billTo" => $billToArray,
    		"product" => $order_info_array);
  
$shipToArray = array("name" => "Jane Smith", 
		"street" => "YorkStreet", 
		"city" => "colombo", 
		"state" => "Sri Lanka" , 
		"zip" => 32343); 

$order_info_array = array("productId" => 2345, 
		"shippingDate" => date(20080101) , 
		"status" => TRUE); 

This same array structure applies for $billToArray as well.

9.1.2. class objects 
In this method user should provide a class map which includes the classes for the complex types defined in the WSDL for the corresponding
    operation he is going to invoke. In this example, you can see that there are four
    complex types associated with the operation QueryPurchaseOrder.
    So the class map implementation should be, 

$class_map = array("complex_type" => "class_name_that_wrapping_the_complex_type");
    
In our example it should be as 

$class_map = array( "OrderInfo" => "OrderInfoWrapper", 
   	                "OrderDetails"  => "OrderDetailsWrapper",
  	                "address" => "addressWrapper", 
	                "productInfo"=> "productInfoWrapper") ;

Each element name in the complex type element will
    be a public attribute in the php class. Then the four classes will be, 

class OrderInfoWrapper{
	public $productName;
 	public $quantity; 
	public $date; 
	public $orderNo; 
} 

class OrderDetailsWrapper{ 
	public $shipTo; 
	public $billTo; 
	public $product; 
} 

class addressWrapper{
   	 public $name; 
   	 public $street; 
    	public $city; 
    	public $state; 
	public $zip; 
} 

class productInfoWrapper{ 
	public $produtId; 
	public $shippingDate; 
	public $status; 
} 


NOTE: You can use the wsdl2php.php script to generate the classes for the WSDL.

After creating the class map it should be passed to WSClient constructor as, $client = new WSClient(array("wsdl"=>"sample_wsdl_11.wsdl", "classmap" => $classmap)); $proxy = $client->getProxy(); Now create an object from the OrderInfoWrapper class and assign values for the attributes. $input_obj = new OrderInfoWrapper(); $input_obj->productName = "WSF/PHP"; $input_obj->quantity = 232; $input_obj->date = date(20040921); $input_obj->orderNo = 20034; Then pass the $input_obj to operation $return_obj = $proxy->QueryPurchaseOrder($input_obj); If the quantity element is another complex type as in the earlier case, then you should create another object from the relevant class and assign the object to quantity attribute. class quantityTypeWrapper{ public $amount; public $weight_kg; } Make sure to have the class mapping entry in classmap array. $quan_obj = new quantityTypeWrapper(); $quan_obj->amount = 23232; $quan_obj->weight_kg = 2323.24; Now the quantity variable in $input_obj object will be $input_obj->quantity = $quan_obj; Since you passed an object to the operation, the returned value will also be an object with the type OrderDetailsWrapper. It has three atrributes shipTo, billTo and product which are also objects of the type addressWrapper, addressWrapper and productInfoWrapper respectively. As an example you can access the values as, $return_obj->billTo->Name; $return_obj->shipTo->street; $return_obj->product->productId;

9.2. Writing a Simple Service to Use the WSDL Mode

We will implement a service to accept the request sent by the above client.

First write a function corresponding to the  QueryPurchaseOrder operation defined in the WSDL.

function QueryPurchaseOrderFunction($productName, $quantity, $date, $orderNo){
            return $return_array;
     }

The return array should be created as the same way it is created in client side.

$return_array = array("shipTo" => $shipToArray,
                                  "billTo" => $billToArray,
                                 "product" => $product_array);

$shipToArray = array("name" => "Jane Smith",
                                "street" => "YorkStreet",
                                 "city" => "colombo",
                                 "zip" => 32343);

$billToArray = array("name" => "John Smith",
                              "street" => "Maple Street",
                              "city" => "LA",
                              "state" => "USA",
                              "zip" => 55432);

$product_array = array("productId" => 2344,
                                   "shippingDate" => date(20080101),
                                    "status" => TRUE);

Now we can map this function to the QueryPurchaseOrderFunction operation as follows.

$operations = array("QueryPurchaseOrder"=>"QueryPurchaseOrderFunction");
 $opparams = array("QueryPurchaseOrderFunction"=>"MIXED");
    
Here we are indicating to the service that the QueryPurchaseOrderFunction is not a function that accepts a WSMessage instance as the sole argument, but a function with mixed arguments.

$service = new WSService(array("wsdl"=>"sample_wsdl_11.wsdl",
                                "operations" => $operations,
                                 "opParams"=>$opParams));
$service->reply();

Similar to the client you can use the class map API for implementing services as well. For the above service you can use the classmap API in the following way.

NOTE: You can use the wsdl2php.php script to generate the classes for the WSDL.

class orderInfo { 
    public $productName; // string
    public $quantity; // float

    public $date; // dateTime
    public $orderNo; // int
}
 

class orderDetails { 
    public $shipTo; // address
    public $billTo; // address

    public $product; // productInfo
}
 
class address { 
    public $name; // string

    public $street; // string
    public $city; // string
    public $state; // string

    public $zip; // decimal
}
 
class productInfo { 
    public $productId; // int

    public $shippingDate; // dateTime
    public $status; // boolean
}
 

// define PHP functions that maps to WSDL operations 
function QueryPurchaseOrder($input) {
    // TODO: fill in the business logic
    // NOTE: $input is of type orderInfo

    // NOTE: should return an object of type orderDetails
 
    $productName = $input->productName;
    $quantity = $input->quantity;
    $date = $input->date;
    $orderNo = $input->orderNo;

 
    // You can process the input with above data,
 
    $shipTo = new address();
    $shipTo->name = "Jane Smith";
    $shipTo->street = "York street";
    $shipTo->city = "Colombo";
    $shipTo->zip = "32234";
    $shipTo->state = "-";

 
    $billTo = new address();
    $billTo->name = "John Smith";
    $billTo->street = "Maple Street";
    $billTo->city = "LA"; 
    $billTo->state = "USA";
    $billTo->zip = 55432;

 
    $product = new ProductInfo();
    $product->productId = 2344;
    $product->shippingDate = date(20080101);
    $product->status = TRUE;

 
 
    $return_value = new orderDetails();
    $return_value->shipTo = $shipTo;
    $return_value->billTo = $billTo;
    $return_value->product = $product;
    return $return_value;

}


// define the class map $class_map
    = array( "orderInfo" => "orderInfo",
    "orderDetails" => "orderDetails",
    "address" => "address",
    "productInfo" => "productInfo"); 

$operations = array( "QueryPurchaseOrder"
    => "QueryPurchaseOrder"); 

// define the actions => operations map 

$actions = array( "http://www.wso2.org/php/QueryPurchaseOrder" => "QueryPurchaseOrder"); 
// create service in WSDL mode 

$service = new WSService(array ("wsdl" =>"sample_wsdl_11.wsdl",
    "actions" => $actions, 
	"classmap" => $class_map, "operations" => $operations)); 
// process client requests and reply 

$service->reply();

9.3. WSDL Generation for a Given Service

WSDL Generation in WSO2 WSF/PHP is done using PHP reflection and an annotation parser. In order to generate a WSDL from a given WSO2 WSF/PHP service, a '?wsdl' request or a '?wsdl2' request should be sent to the server. '?wsdl' serves WSDL version 1.1, and '?wsdl2' serves WSDL version 2.0. For example, if you want to generate the WSDL for the service echoService.php, a request should be sent as,

http://localhost/echoService.php?wsdl

or

http://localhost/echoService.php?wsdl2

For more information on the annotation syntax, please have a look at the WSDL generation API document.

9.4. WSDL to PHP Code Generation

You can Generate the PHP code for a given WSDL using wsdl2php.php script bundle with the WSO2 WSF/PHP distribution. You can find the wsdl2php.php script inside the 'scripts' directory.

Here is how you can use this script.

Usage: php wsdl2php.php [-s] <wsdl> 
    wsdl - URL or path to WSDL file

    Options:
        -s        Generate code for service.
                  By default this is false, meaning code for client will be generated.

Eg
Consider you have a wsdl named purchaseOrder.wsdl and you want to code generate for that. 

The following command will generate your client code to a file named purchase_client.php.

php wsdl2php.php purchaseOrder.wsdl > purchase_client.php

To generate the service  use

php wsdl2php.php -s purchaseOrder.wsdl > purchase_service.php