beta it republik » Articles

Articles

Untitled Document
Monday, 18 August 2008 | Article

WSDLs and PHP Web Service Clients and Servers

If you are planning to expose functions in PHP to other languages or if you have to consume web services written in a different computer language and running in an environment that’s not yours, chances are at some point you’ll have to familiarize yourself with WSDL files. Or maybe your manager read about them in a magazine and has been using the acronym constantly in meetings as a suggestion, like “Hey, maybe if we use a WSDL here it will help.” Now since your manager’s interested, you’d better come up to speed on how to quickly write clients and servers for web services in PHP when someone e-mails you one of them ‘Wizzdul’ things. Fortunately, extensions to PHP like the SOAP extensions will allow you to write the clients or even servers pretty easily. That’s what this article is about—starting off with a WSDL and quickly writing some clients and servers (in a couple different ways) in PHP. You’ll see how it’s done using both PHP’s SOAP extensions and the PEAR::SOAP package.


Writing a Web Service Contract
Web services certainly have their advantages. One of them is that a properly written service allows you to expose methods you have written for a variety of different clients, and have the benefit of being blissfully agnostic about how the clients are implemented.

This article is about exposing web services written in PHP. The approach we’ll take in this article is to follow the model of contract-first web service development. So we’ll be creating the Web Service Description Language (WSDL) file first, making sure it’s good to go, and then writing the PHP to back it up.

Contract-first development is just one way to approach making web services, but it does have certain advantages. One is that you can make the contract and then be free to decide later how you will implement it—PHP, Perl, Java, whatever. Another advantage is that, since a contract is written, it is easier to begin work on both the client and the server and have them meet in the middle. If your team has a WSDL to use when building web services, one part of the team can work on the server while the other works on the client. Providing they both adhere to the contract (read WSDL), everything should be rosy and wonderful when the two parts come back together.


In this article, we will start by going right into what WSDLs are and how to build them. To get the best out of what we’re covering, you should already be familiar with PHP basics and have a fairly good understanding of what web services are.

WSDL Basics
The WSDL file is just an XML file structured a certain way and given an awkward acronym. More than that, however, WSDL’s structure describes everything there is to know about a service that has been exposed to the Web. The WSDL tells the client what data types to expect, what method names it will find, and provides implementation details about how the given service is exposed. We’ll dive into writing a simple WSDL in the next few sections of this article—you’ll be able to build and debug web services much better once you know how all the parts come together.

Listing 1
<?xml version =“1.0” encoding =“UTF-8” ?>
<defi nitions name=“EchoWebService”
targetNamespace=“urn:EchoWebService”
xmlns:tns=“urn:EchoWebService”
xmlns:soap=“http://schemas.xmlsoap.org/wsdl/soap/”
xmlns:xsd=“http://www.w3.org/2001/XMLSchema”
xmlns:soapenc=“http://schemas.xmlsoap.org/soap/encoding/”
xmlns:wsdl=“http://schemas.xmlsoap.org/wsdl/”
xmlns=“http://schemas.xmlsoap.org/wsdl/”>
<message name=“echoMessageRequest”>
<part name=“message” type=“xsd:string”/>
</message>
<message name=“echoMessageResponse”>
<part name=“result” type=“xsd:string”/>
</message>
<portType name=“EchoPortType”>
<operation name=“echoMessage”>
<input message=“tns:echoMessageRequest”/>
<output message=“tns:echoMessageResponse”/>
</operation>
</portType>
<binding name=“EchoBinding”
type=“tns:EchoPortType”>
<soap:binding style=“rpc”
transport=“http://schemas.xmlsoap.org/
soap/http”/>
<operation name=“echoMessage”>
<soap:operation soapAction=
“urn:EchoWebService#echoMessage”/>
<input>
<soap:body use=“encoded”
namespace=“urn:EchoWebService”
encodingStyle=“http://
schemas.xmlsoap.org/soap/encoding/”/>
</input>
<output>
<soap:body use=“encoded”
namespace=“urn:EchoWebService”
encodingStyle=“http://
schemas.xmlsoap.org/soap/encoding/”/>
</output>
</operation>
</binding>
<service name=“EchoService”>
<port name=“EchoPort” binding=“EchoBinding”>
<soap:address location=“http://servername/path/to/fi le/echows.
php”/>
</port>
</service>
</definitions>


For more information about WSDL files, one of the best resources is the W3C page, http://www.w3.org/TR/wsdl.

A Simple WSDL
We’ll jump right into looking at a simple WSDL, then begin breaking it down to look at the parts. The example shown in Listing 1 is a simple WSDL that will send a message from the client, which will in turn be echoed from the server back to the client. It contains a single method called echo(), a function called echoMessage that accepts a string and bounces the string back.

types
The example WSDL file given here doesn’t have a types section. The simple example (in Listing 1) includes only a standard data type: string. If you have a complex type, like a structure that you have made yourself containing strings and integers and other types, the complex type description would be included in the ‘types’ section.

message
The messages describe what is inside the ‘payload’ being sent to the server and back to the client.

The message section for the request describes how one of the messages to the web service will be constructed. Later in the file, the portType section of the WSDL file will refer to the message by name when describing how the input is put together. Here is the message section:

<message name= “echoMessageRequest”>
<part name= “message” type=“xsd:string”/>
</message>


The message section includes a reference to the type. The types used in this example are simple strings, but if you had a complex type—an object composed of many other simple types or even an object made of other objects—you could reference the type described here.

portType
This section of the WSDL refers to other parts of the file to assemble them into a complete description of the operations available to the web service:


<portType name= “EchoPortType”>
<operation name= “echoMessage”>
<input message= “tns:echoMessageRequest”/>
<output message= “tns:echoMessageResponse”/>
</operation>
</portType>


The portType section has a name, EchoPortType, and describes an operation named echoMessage. The echoMessage operation is a request-response operation, which means it will expect an input (request) and provide an output (response) to the client that called the operation.

As an input, the echo operation expects to see the message described by the echoMessageRequest message. The operation will then put out a message described in the message section named echoMessageResponse.

binding
The binding section describes the message format and the protocol through which the message is available:

<binding name=“EchoBinding”
type=“tns:EchoPortType”>
<soap:binding style=“rpc”
transport= “http://schemas.xmlsoap.org/
soap/http”/>
<operation name=“echoMessage”>
<soap:operation soapAction=
“urn:EchoWebService#echoMessage”/>
<input>
<soap:body use=“encoded”
namespace=“urn:EchoWebService”
encodingStyle=“http://
schemas.xmlsoap.org/soap/encoding/”/>
</input>
<output>
<soap:body use=“encoded”
namespace=“urn:EchoWebService”
encodingStyle=“http://
schemas.xmlsoap.org/soap/encoding/”/>
</output>
</operation>
</binding>


In the sample file, the binding protocol is Simple Object Access Protocol (SOAP). The operation is a pointer to the operation defined in the portType section. So, in the sample file, echoMessage in the binding section provides binding details for the echoMessage operation described in the portType section. There can be one or more bindings for any portType. The binding section is just saying, “Take this opertation X and make it available through protocol Y.”

ports
Finally, there must be some way in the WSDL file to provide the actual URI of the web service. This is where the port section comes in handy:

<service name=“EchoService”>
<port name=“EchoPort” binding=“EchoBinding”>
<soap:address location=“http://servername/path/to/fi le/echows.php”/>
</port>
</service>


In this example, the binding identified by EchoBinding is specified at the URL http://servername/path/to/file/echows.php. If you are using this file, replace the servername with your real machine name, and replace the directory path/to/file/echows.php with the location and file name of the PHP file that will handle the actual request.

Validating the WSDL
Since the WSDL is supposed to be a valid XML file, the first step for validation purposes is to run the XML file through a tool to ensure the XML file is well-formed. There are many IDEs and applications that provide this functionality; when I’m using the UNIX command line, I use a tool called xmllint , which will point out any missing elements or otherwise poorly-formed XML.

Using SOAP Extensions
SOAP Extensions are included with PHP if it was compiled with the – enable-soap option. If you’re unsure what options your version of PHP was compiled with, use the phpinfo() method to print out the information about PHP, or use php -i at the command prompt. If you see – enable-soap in the Configure Command, you’re in luck. The SOAP extensions are available in versions 5.0 and newer of PHP. The code shown in Listing 2 will allow you to expose our sample WSDL:

Listing 2
<?php
function echoMessage($message)
{
return $message;
}
ini_set(“soap.wsdl_cache_enabled”, “0”);
$server = new SoapServer(“echo.wsdl”);
$server->addFunction(“echoMessage”);
$server->handle();
?>


This script is saved in a file called echows.php in the location you specified in the ports section of the WSDL file—you will be able to call this web service from a client and get the echo response.

The echoMessage function you see in this file should look familiar—it has the same name as the operation in the portType section of the WSDL. It also has the same parameter—message, which is a string. It returns the same string, which will echo it to the client. When I was testing this on my own server I added the strrev function to echo the string backwards—just as a very easy test to ensure everything was working as I thought it was.

Listing 3
<?php
require_once ‘SOAP/Server.php’;
require_once ‘SOAP/Disco.php’;
class EchoWebService
{
var $__dispatch_map = array();
function EchoWebService()
{
$this->__dispatch_map[‘echoMessage’] =
array ( ‘in’ => array ( ‘message’=> ‘string’ ),
‘out’ => array ( ‘result’ => ‘string’ ) );
}
function echoMessage($message)
{
return “PEAR::SOAP says: ” . $message;
}
}
$server = new SOAP_Server();
$service = new EchoWebService();
$server->addObjectMap($service, ‘urn:EchoWebService’);
$server->service($HTTP_RAW_POST_DATA);
exit;
?>


The ini_set function in this file sets the soap.wsdl_cache_enabled value to 0, which disables server caching of the WSDL.One of the reasons to do this during development is that if you make any changes to the WSDL you’ll want to see the changes right away.

As you can see, using the SOAP extensions in PHP to create a SOAP server based off a WSDL is a snap. Next, we’ll take a look at creating a SOAP server using the PEAR::SOAP package.

Using Pear::SOAP
At the time of writing, the PHP Extension and Application Repository (PEAR) included a SOAP package in beta. Using it to build a server has a few more steps than building a web service server using the SOAP extensions. Moreover, the PEAR:: SOAP package is written in PHP, while the PHP extensions are written in C. This will make the PEAR package perform a little slower. On the other hand, you don‘t need to have PHP compiled and configured a certain way in order to use the PEAR packages. Using packages may be a better option if you have an ISP that does not have the SOAP extensions enabled. To install the SOAP package, you’ll need to specify ‘beta’ if you have PEAR configured to prefer stable packages only. To install the SOAP package, type:

$ pear install SOAP-beta

The SOAP package will require HTTP_Request. Mail, Mail_Mime, and Net_DIME are optional packages.

Listing 3 holds an example of the SOAP server class. It is called echows-pear.php to distinguish it from the first echows.php class. As you can see, there is a little more work involved in this method of using the SOAP package. However, it still works
seamlessly with the same clients just by switching the location in the WSDL, like this:

<service name=“EchoService”>
<port name=“EchoPort” binding=“EchoBinding”>
<soap:address location=“http://servername/path/to/fi le/echows-pear.php”/>
</port>
</service>
The server contains a class called EchoWebService, as shown here:
class EchoWebService
{
...
}


The class contains a function called echoMessage, similar to the function in the echows.php file. The addObjectMap function of the server will map the object and function to the WSDL. So, it is important that the function name is the same as the operation name (echoMessage) in the WSDL. Additionally, the namespace given in the addObjectMap function must match the namespace given in the WSDL file (urn:EchoWebService in these examples).

Making a Client
Now that we have a web service implemented a couple different ways, it will be useful to get a client up and running to test the web service. The following lines of code show how to use the SOAP extensions for making a client that works based off a WSDL:

<?php
$client = new SoapClient(“echo.wsdl”);
try
{
print ($client->echoMessage(“Hello, there!”));
} catch (SoapFault $fault) {
print $fault;
}
?>


Save these lines in a file called echo-test.php. When you access the file, the SoapClient class will access the WSDL given to the SoapClient constructor as a URL or relative path. The client will invoke the echoMessage function, passing it “Hello there!” as the string message.

Listing 4
<?php
function getQuote()
{
$quotefi le = “incl/quotes.txt”;
if ($quotes = @fi le($quotefile)) {
$quote = rand(0, sizeof($quotes) - 1);
return $quotes[$quote];
} else {
return “My default quote here.”;
}
}
ini_set(“soap.wsdl_cache_enabled”, “0”);
$server = new SoapServer(“quotes.wsdl”);
$server->addFunction(“getQuote”);
$server->handle();
?>


The Quotes Method
The Echo service is useful to ensure that everything is working, but let’s take a look at doing something different. Listing 4 shows a simple snippet of code that grabs a random line from a file—it will be useful in our second example of a quote-of-the-day web service. Notice the differences between this one and the echows.php file—the getQuote method replaces the echoMessage method and the WSDL file’s name has been changed to a new name. The new file is shown in Listing 5.

The major differences between this file and the previous WSDL file are few—the names of the operations and namespaces have changed. Also, the getQuote operation doesn’t require a parameter passed to it anymore, so the quoteRequest element no longer has a part child element. Since most of the pieces in the file are necessary for it to be valid, the rest of the file is pretty much unchanged.

Listing 5
<?xml version =“1.0” encoding =“UTF-8” ?>
<definitions name=“QuoteService”
targetNamespace=“urn:QuoteService”
xmlns:tns=“urn:QuoteService”
xmlns:soap=“http://schemas.xmlsoap.org/wsdl/soap/”
xmlns:xsd=“http://www.w3.org/2001/XMLSchema”
xmlns:soapenc=“http://schemas.xmlsoap.org/soap/encoding/”
xmlns:wsdl=“http://schemas.xmlsoap.org/wsdl/”
xmlns=“http://schemas.xmlsoap.org/wsdl/”>
<message name=“quoteRequest” />
<message name=“quoteResponse”>
<part name=“result” type=”xsd:string”/>
</message>
<portType name=“QuotePortType”>
<operation name=“getQuote”>
<input message=“tns:quoteRequest”/>
<output message=“tns:quoteResponse”/>
</operation>
</portType>
<binding name=“QuoteBinding” type=“tns:QuotePortType”>
<soap:binding style=“rpc”
transport=“http://schemas.xmlsoap.org/soap/http”/>
<operation name=“getQuote”>
<soap:operation soapAction=“urn:QuoteService#getQuote
”/>
<input>
<soap:body use=“encoded” namespace=“urn:QuoteService”
encodingStyle=“http://schemas.xmlsoap.org/soap/
encoding/”/>
</input>
<output>
<soap:body use=“encoded” namespace=“urn:QuoteService”
encodingStyle=“http://schemas.xmlsoap.org/soap/
encoding/”/>
</output>
</operation>
</binding>
<service name=“QuoteService”>
<port name=“QuotePort” binding=“QuoteBinding”>
<soap:address location=“http://servername/path/to/fi le/
quotews.php”/>
</port>
</service>
</definitions>


A Quote-of-the-Day Client
Lastly, a quick client will allow us to see the results of the new web service. The client script looks just like the echo-client. php script, with the name of the WSDL and the function names changed:

<?php
$client = new SoapClient(“quotes.wsdl”);
try
{
print ($client->getQuote());
} catch (SoapFault $fault) {
print $fault;
}
?>


Another version of the client, using the PEAR::SOAP package, looks very similar:

<?php
require_once ‘SOAP/Client.php’;
$wsdl = new SOAP_WSDL(‘quotes.wsdl’);
$myobj = $wsdl->getProxy();
print( $myobj->getQuote() );
?>


Summary
In this article, we took a quick view at the basic structure of WSDL files, and looked at two different methods of building both servers and clients based off the WSDL file. Generating the WSDL first and then writing the code is doing contract-first web service development, a methodology which can be implemented in any language—not just PHP

The built-in SOAP extensions make it easy to get a web service server up and running, as well as a client. If the SOAP extensions are unavailable, the PEAR::SOAP packages offer a solid means of accomplishing the same task. There is much more to the PEAR::SOAP library than was covered here in this article, so make sure to keep an eye on that package as it comes out of beta and has more documentation.


About the Author

Nathan A. Good lives in the Twin Cities area in Minnesota. He is a contractor with Alliance of Computer Professionals in Bloomington. When he isn’t writing software, Nathan enjoys building PCs and servers, reading about and working with new technologies, and trying to get all his friends to make the move to open source software. When he’s not at a computer (which he admits is not often), he spends time with his family, at his church, and at the movies.


   Related Links
WSDL Specification
PEAR web site
PHP SOAP Extensions


Comment

Name:

Comment:

Captcha Verification !
captcha_image