Creating Web Services with PHP and SOAP, Part 1 As application developers, the ability to develop software and services for a wide range of platforms is a necessary skill, but not everyone uses the same language or platform and writing code to support them all is not feasible. If only there was a standard that allowed us to write code once and allow others to interact with it from their own software with ease. Well luckily there is… and it’s name is SOAP. (SOAP used to be an acronym which stood for Simple Object Access Protocol, but as of version 1.2 the protocol goes simply by the name SOAP.) SOAP allows you to build interoperable software and allows others to take advantage of your software over a network. It defines rules for sending and receiving Remote Procedure Calls (RPC) such as the structure of the request and responses. Therefore, SOAP is not tied to any specific operating system or programming language. As that matters is someone can formulate and parse a SOAP message in their chosen language In this first of a two part series on web services I’ll talk about the SOAP specification and what is involved in creating SOAP messages. I’ll also demonstrate how to create a SOAP server and client using the excellent NuSOAP library to illustrate the flow of SOAP. In the second part I’ll talk about the importance of WSDL files, how you can easily generate them with NuSOAP as well, and how a client may use a WSDL file to better understand your web service.
The Structure of a SOAP Message SOAP is based on XML so it is considered human read, but there is a specific schema that must be adhered to. Let’s first break down a SOAP message, stripping out all of its data, and just look at the specific elements that make up a SOAP message. 01 <?xml version="1.0"?> 02 <soap:Envelope 03 xmlns:soap="http://www.w3.org/2001/12/soap-envelope" 04 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> 05 <soap:Header> 06 ... 07 </soap:Header> 08 <soap:Body> 09 ...
10 <soap:Fault> 11 ... 12 </soap:Fault> 13 </soap:Body> 14 </soap:Envelope> This might look like just an ordinary XML file, but what makes it a SOAP message is the root element Envelope with the namespace soap as http://www.w3.org/2001/12/soapenvelope. The soap:encodingStyle attribute determines the data types used in the file, but SOAP itself does not have a default encoding. soap:Envelope is mandatory, but the next element, soap:Header, is optional and usually contains information relevant to authentication and session handling. The SOAP protocol doesn’t offer any built-in authentication, but allows developers to include it in this header tag. Next there’s the required soap:Body element which contains the actual RPC message, including method names and, in the case of a response, the return values of the method. The soap:Fault element is optional; if present, it holds any error messages or status information for the SOAP message and must be a child element of soap:Body. Now that you understand the basics of what makes up a SOAP message, let’s look at what SOAP request and response messages might look like. Let’s start with a request. 01 <?xml version="1.0"?> 02 <soap:Envelope 03 xmlns:soap="http://www.w3.org/2001/12/soap-envelope" 04 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> 05 <soap:Body xmlns:m="http://www.yourwebroot.com/stock"> 06 <m:GetStockPrice> 07 <m:StockName>IBM</m:StockName> 08 </m:GetStockPrice> 09 </soap:Body> 10 </soap:Envelope> Above is an example SOAP request message to obtain the stock price of a particular company. Inside soap:Body you’ll notice the GetStockPrice element which is specific to the application. It’s not a SOAP element, and it takes its name from the function on the server that will be called for this request. StockName is also specific to the application and is an argument for the function. The response message is similar to the request: 01 <?xml version="1.0"?> 02 <soap:Envelope 03 xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
04 soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"> 05 <soap:Body xmlns:m="http://www.yourwebroot.com/stock"> 06 <m:GetStockPriceResponse> 07 <m:Price>183.08</m:Price> 08 </m:GetStockPriceResponse> 09 </soap:Body> 10 </soap:Envelope> Inside the soap:Body element there is a GetStockPriceResponse element with a Price child that contains the return data. As you would guess, both GetStockPriceResponse and Price are specific to this application. Now that you’ve seen an example request and response and understand the structure of a SOAP message, let’s install NuSOAP and build a SOAP client and server to demonstrate generating such messages.
Building a SOAP Server It couldn’t be easier to get NuSOAP up and running on your server; just visit sourceforge.net/projects/nusoap, download and unzip the package in your web root direoctry, and you’re done. To use the library just include the nusoap.php file in your code. For the server, let’s say we’ve been given the task of building a service to provide a listing of products given a product category. The server should read in the category from a request, look up any products that match the category, and return the list to the user in a CSV format. Create a file in your web root named productlist.php with the following code: 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
<?php require_once "nusoap.php"; function getProd($category) { if ($category == "books") { return join(",", array( "The WordPress Anthology", "PHP Master: Write Cutting Edge Code", "Build Your Own Website the Right Way")); } else { return "No products listed under that category"; } }
16 $server = new soap_server(); 17 $server->register("getProd"); 18 $server->service($HTTP_RAW_POST_DATA); First, the nusoap.php file is included to take advantage of the NuSOAP library. Then, the getProd() function is defined. Afterward, a new instance of the soap_server class is instantiated, the getProd() function is registered with its register() method. This is really all that’s needed to create your own SOAP server – simple, isn’t it? In a real-world scenario you would probably look up the list of books from a database, but since I want to focus on SOAP, I’ve mocked getProd() to return a hard-coded list of titles. If you want to include more functionality in the sever you only need to define the additional functions (or even methods in classes) and register each one as you did above. Now that we have a working server, let’s build a client to take advantage of it.
Building a SOAP Client Create a file named productlistclient.php and use the code below: 01 <?php 02 require_once "nusoap.php"; $client = 03 nusoap_client("http://localhost/nusoap/productlist.php"); 04 05 $error = $client->getError(); 06 if ($error) { 07 echo "<h2>Constructor error</h2><pre>" . $error . "</pre>"; 08 } 09 10 $result = $client->call("getProd", array("category" => "books")); 11 12 if ($client->fault) { 13 echo "<h2>Fault</h2><pre>"; 14 print_r($result); 15 echo "</pre>"; 16 } 17 else { 18 $error = $client->getError(); 19 if ($error) { 20 echo "<h2>Error</h2><pre>" . $error . "</pre>"; 21 }
new
22 else { 23 echo "<h2>Books</h2><pre>"; 24 echo $result; 25 echo "</pre>"; 26 } 27 } Once again we include nusoap.php with require_once and then create a new instance of nusoap_client. The constructor takes the location of the newly created SOAP server to connect to. The getError() method checks to see if the client was created correctly and the code displays an error message if it wasnâ&#x20AC;&#x2122;t. The call() method generates and sends the SOAP request to call the method or function defined by the first argument. The second argument to call() is an associate array of arguments for the RPC. The fault property and getError() method are used to check for and display any errors. If no there are no errors, then the result of the function is outputted. Now with both files in your web root directory, launch the client script (in my case http://localhost/nusoap/productlistclient.php) in your browser. You should see the following:
If you want to inspect the SOAP request and response messages for debug purposes, or if you just to pick them apart for fun, add these lines to the bottom of productlistclient.php: 1 echo "<h2>Request</h2>"; echo "<pre>" . htmlspecialchars($client->request, 2 "</pre>"; 3 echo "<h2>Response</h2>";
ENT_QUOTES)
.
4
echo
"<pre>"
.
htmlspecialchars($client->response,
ENT_QUOTES)
.
"</pre>"; The HTTP headers and XML content will now be appended to the output.
Summary In this first part of the series you learned that SOAP provides the ability to build interoperable software supporting a wide range of platforms and programming languages. You also learned about the different parts of a SOAP message and built your own SOAP server and client to demonstrate how SOAP works. In the next part I’ll take you deeper into the SOAP rabbit hole and explain what a WSDL file is and how it can help you with the documentation and structure of your web service.
Creating Web Services with PHP and SOAP, Part 2 In the first part of this series, I showed you how developing applications with the SOAP protocol is a great way to build interoperable software. I also demonstrated how easy it is to build your very own SOAP server and client using the NuSOAP library. This time around I’d like to introduce you to something that you will most definitely run into when working with SOAP – WSDL files. In this article we’ll talk about what WSDL files are and how to use them. I’ll show you how to quickly build your WSDL files with NuSOAP and incorporate a WSDL file into the SOAP server and client examples from the first part.
What are WSDL Files? Web Services Description Language (WSDL) files are XML documents that provide metadata for a SOAP service. They contain information about the functions or methods the application makes available and what arguments to use. By making WSDL files available to the consumers of your service, it gives them the definitions they need to send valid requests precisely how you intend them to be. You can think of WSDL files as a complete contract for the application’s communication. If you truly want to make it easy for others to consume your service you will want to incorporate WSDL into your SOAP programming.
WSDL Structure Just like SOAP messages, WSDL files have a specific schema to adhere to, and specific elements that must be in place to be valid. Letâ&#x20AC;&#x2122;s look at the major elements that make up a valid WSDL file and explain their uses. 01 <definitions> 02 <types> 03 ........ 04 </types> 05 <message> 06 <part></part> 07 </message> 08 <portType> 09 ....... 10 </portType> 11 <binding> 12 .... 13 </binding> 14 <service> 15 .... 16 </service> 17 </definitions> The root element of the WSDL file is the definitions element. This makes sense, as a WSDL file is by definition a definition of the web service. The types element describes the type of data used, which in the case of WSDL, XML schema is used. Within the messages element, is the definition of the data elements for the service. Each messages element can contain one or more part elements. The portType element defines the operations that can be performed with your web service and the request response messages that are used. Within the binding element, contains the protocol and data format specification for a particular portType. Finally, we have the service element which defines a collection of The terminology has changed slightly in naming some of the elements in the WSDL 2.0 specification. portType, for example, has changed its name to Interface. Since support for WSDL 2.0 is weak, I've chosen to go over version 1.1 which is more widely used.
Building a WSDL File WSDL files can be cumbersome to write by hand as they must contain specific tags and are usually quite long. The nice thing about using NuSOAP is that it can create a WSDL file for you! Let's modify the SOAP server we made in the first article to accommodate this. Open productlist.php and change it to reflect the code below: 01 <?php 02 require_once "nusoap.php"; 03 04 function getProd($category) { 05 if ($category == "books") { 06 return join(",", array( 07 "The WordPress Anthology", 08 "PHP Master: Write Cutting Edge Code", 09 "Build Your Own Website the Right Way")); 10 } 11 else { 12 return "No products listed under that category"; 13 } 14 } 15 16 $server = new soap_server(); 17 $server->configureWSDL("productlist", "urn:productlist"); 18 19 $server->register("getProd", 20 array("category" => "xsd:string"), 21 array("return" => "xsd:string"), 22 "urn:productlist", 23 "urn:productlist#getProd", 24 "rpc", 25 "encoded", 26 "Get a listing of products by category"); 27 28 $server->service($HTTP_RAW_POST_DATA); Basically this is the same code as before but with only a couple of changes. The first change adds a call to configureWSDL(); the method acts as a flag to tell the server to generate a WSDL file for our service. The first argument is the name of the service and the second is the namespace for our service. A discussion of namespaces is really outside the scope of this article, but be aware that although we are not taking advantage of them here, platforms like Apache Axis and .NET do. It's best to include them to be fully interoperable.
The second change adds additional arguments to the register() method. Breaking it down: • getProd is the function name • array("category" => "xsd:string") defines the input argument to getProd and its data type • array("return" => "xsd:string") defines the function's return value and its data type • urn:productlist defines the namespace • urn:productlist#getProd defines the SOAP action • rpc defines the type of call (this could be either rpc or document) • encoded defines the value for the use attribute (encoded or literal could be used) • The last parameter is a documentation string that describes what the getProd function does Now point your browser to http://yourwebroot/productlist.php?wsdl and you'll see the brand new WSDL file created for you. Go ahead and copy that source and save it as it’s own file called products.wsdl and place it in you web directory.
Consuming WSDL Files with the Client We've modified the SOAP server to generate a WSDL file, so now lets modify the SOAP client to consume it. Open up productlistclient.php created in the previous article and simply change the line that initiates the client from this: 1 $client = new nusoap_client("http://localhost/nusoap/productlist.php"); to this: 1 $client = new nusoap_client("products.wsdl", true); The second parameter in the nusoap_client() constructor call tells NuSOAP to build a SOAP client to accept the WSDL file. Now launch productlistclient.php in your browser and you should see the same result as before, but now you're using WSDL power!
Summary In part 2 of this series on creating web services with PHP and SOAP, we went over the importance of using WSDL files for optimum interoperability. We talked about the different elements that make up a WSDL file and their definitions, and then I showed you how to quickly and easily create your own WSDL files with the NuSOAP library. Finally, we modified our SOAP server and client to demonstrate how to use WSDL in your applications.
As you can probably guess, I've just barely scraped the surface of what SOAP can do for you, but with these new tools you can provide an easy and well-accepted way of exposing web services to your users.