Creating the SOAP server
Create a file called food.php and copy the and paste the below code into it.
<?php require_once "lib/nusoap.php"; class food { public function getFood($type) { switch ($type) { case 'starter': return 'Soup'; break; case 'Main': return 'Curry'; break; case 'Desert': return 'Ice Cream'; break; default: break; } } } $server = new soap_server(); $server->configureWSDL("foodservice", "http://www.greenacorn-websolutions.com/foodservice"); $server->register("food.getFood", array("type" => "xsd:string"), array("return" => "xsd:string"), "http://www.greenacorn-websolutions.com/foodservice", "http://www.greenacorn-websolutions.com/foodservice#getFood", "rpc", "encoded", "Get food by type"); @$server->service($HTTP_RAW_POST_DATA);
Now lets break this down!
- The first section of the above code is requiring the NuSOAP library.
- The second part is a simple PHP class with one method called getFood
- Next we initialise the soap_server class, configure the WDSL and register the class method.
Okay, so what are all those register parameters?
- food.getFood is the className.methodName
- array(“type” => “xsd:string”) defines the input argument to getFood and its data type i.e string, number, boolean
- array(“return” => “xsd:string”) defines the method’s return value and its data type i.e string, number, boolean
- http://www.greenacorn-websolutions.com/foodservice defines the namespace. Using a URI to identify a namespace, rather than a simple string (such as “food:webservice”), reduces the probability of different namespaces using duplicate identifiers.
- http://www.greenacorn-websolutions.com/foodservice#getFood defines the SOAP action
- rpc defines the type of call (this could be either rpc or document)
- encoded defines the value for the attribute (encoded or literal could be used)
- The last parameter is a documentation string that describes what the getFood method does
The WSDL
Now navigate to your web application via the browser. In my case I navigated to http://soap-test.dev/food.php?wsdl. Just be sure to include the ?wsdl. You should see an xml document like the one below. Save a copy of this in you web root and call it food.wsdl.
<?xml version="1.0" encoding="ISO-8859-1"?> <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://www.greenacorn-websolutions.com/foodservice" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://www.greenacorn-websolutions.com/foodservice"> <types> <xsd:schema targetNamespace="http://www.greenacorn-websolutions.com/foodservice"> <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /> </xsd:schema> </types> <message name="food.getFoodRequest"> <part name="type" type="xsd:string" /> </message> <message name="food.getFoodResponse"> <part name="return" type="xsd:string" /> </message> <portType name="foodservicePortType"> <operation name="food.getFood"> <documentation>Get food by type</documentation> <input message="tns:food.getFoodRequest"/> <output message="tns:food.getFoodResponse"/> </operation> </portType> <binding name="foodserviceBinding" type="tns:foodservicePortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <operation name="food.getFood"> <soap:operation soapAction="http://www.greenacorn-websolutions.com/foodservice#getFood" style="rpc"/> <input> <soap:body use="encoded" namespace="http://www.greenacorn-websolutions.com/foodservice" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" namespace="http://www.greenacorn-websolutions.com/foodservice" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="foodservice"> <port name="foodservicePort" binding="tns:foodserviceBinding"> <soap:address location="http://soap-test.dev/food.php"/> </port> </service> </definitions>
Creating the SOAP client
Create a file called foodclient.php and copy the and paste the below code into it.
<?php require_once "lib/nusoap.php"; $client = new nusoap_client("food.wsdl", true); $error = $client->getError(); if ($error) { echo "<h2>Constructor error</h2><pre>" . $error . "</pre>"; } $result = $client->call("food.getFood", array("type" => "Main")); if ($client->fault) { echo "<h2>Fault</h2><pre>"; print_r($result); echo "</pre>"; } else { $error = $client->getError(); if ($error) { echo "<h2>Error</h2><pre>" . $error . "</pre>"; } else { echo "<h2>Main</h2>"; echo $result; } } // show soap request and response echo "<h2>Request</h2>"; echo "<pre>" . htmlspecialchars($client->request, ENT_QUOTES) . "</pre>"; echo "<h2>Response</h2>"; echo "<pre>" . htmlspecialchars($client->response, ENT_QUOTES) . "</pre>";
Now lets break this down too!
- Initialise nusoap_client and tell it to accept a WSDL file. We could also do this: $client = new nusoap_client(“http://soap-test.dev/food.php”); However, using a WSDL file is considered best practice.
- The getError method checks if their were any errors whilst creating the client.
- The call method defines the RPC method and arguments and sends the SOAP request.
- Errors are again checked and the results are outputted.
- The last few lines of code are used to show the SOAP request and response (ideal for debugging)
Now let’s test
Navigate to your food client, in my case http://soap-test.dev/foodclient.php. You should now see results similar to the below:
Working with a proxy class
A proxy class is code that looks exactly like the class it meant to represent; however the proxy class doesn’t contain any of the application logic. Instead, the proxy class contains marshalling and transport logic.
To use a proxy class with the food soap client. Simple modify foodclient.php to look like the below:
<?php require_once "lib/nusoap.php"; $client = new nusoap_client("food.wsdl", true); $proxy = $client->getProxy(); echo $proxy->food__getFood('Main');
Due to the fact our methods are a part of the food class, we will need to prefix the proxy methods with food__.