Menu

Creating and Consuming Web Services With PHP

March 24, 2004

Jean-Luc David

Web services are widely supported by all the major technology vendors and organizations including IBM, Microsoft, Sun Microsystems and the W3C. Millions of dollars have been invested in the development of this technology. Web services allow you to share data across many platforms and hardware configurations. For example, you can create a Java web service and someone else can consume it using a .NET client without having to learn one line of Java code.

There are many available scripting languages that support web services. PHP is one such language, with a powerful arsenal of open source functions and tools.

In this article, we will compare and contrast three methods of consuming and producing web services: XML-RPC, SOAP and REST. This article assumes that the reader has a basic knowledge of both web service specifications and PHP. You must also have access to a PHP-enabled web server to implement our code samples. If you require more information, please refer to the resource section at the end of the article.

XML-RPC

Remote Procedure Calls are used to establish and facilitate transactions between two remote systems. Example of popular RPC implementations include DCOM and CORBA. XML-RPC is an established implementation of RPC that allows you to transport XML encoded data between two servers using HTTP. In the following examples, we will use an implementation of XML-RPC originally developed by Edd Dumbill ( http://xmlrpc.usefulinc.com/php.html).

To enable PHP XML-RPC functionality, you must download the XML-RPC toolkit available at the following link: http://sourceforge.net/project/showfiles.php?group_id=34455

Creating an XML-RPC Web service

The main include files we will be using are xmlrpc.inc (the base class library) and xmlrpcs.inc (the server class library). Here is how you implement a simple XML-RPC server using PHP. First, we bring in both the client and server libraries using include statements:


<?

include("xmlrpc.inc");

include("xmlrpcs.inc");

Then we define a new function called onttax. This function will be the backbone of a web service that will calculate the 15% Federal & Provincial sales tax for Ontario Canada based on a dollar amount. A parameter, which corresponds to the dollar amount, is passed into the function. The parameter is then converted to a scalar variable. Once the calculation is completed, a response is created (using the xmlrpcresp class) returning the value of the sales tax:


function onttax($par){

$amount=$par->getParam(0);

$amountval=$amount->scalarval(); 

$taxcalc=$amountval*.15;

return new xmlrpcresp(new xmlrpcval($taxcalc, "string"));

}

Then we can instantiate the server and serialize our onttax function back to the caller.


$server=new xmlrpc_server(array("taxcalc.onttax"=>array("function"=>"onttax")));

?>

Consuming an XML-RPC Web service

Now that we have built a server, the next step is to develop a client to call our web service. First, we need to bring in the base class library and define a scalar variable called $amount and assign it a value of $15.00:


<?

include("xmlrpc.inc");

$amount="15.00";

Then we instantiate our XML-RPC client which will connect to our new server. The value of $amount is passed to the server using the xmlrpcmsg object:


$format=new xmlrpcmsg('taxcalc.onttax',

            array(new xmlrpcval($amount, "double")));

$client=new xmlrpc_client("/xmlrpc-server.php", "localhost", 80);

Once a connection is made, the request is sent to the server. The response, which corresponds to the sales tax calculation, is then passed along to $value. $value is then converted to a scalar variable and returned to the user. Of course there is no need to stop there, you can easily create a custom client which manipulates the data returned from the server:


$request=$client->send($format);

$value=$request->value();

print $value->scalarval();

?>

XML-RPC is a simple, effective method of transmitting XML data. It has been implemented in projects such as FreeNET, O'Reilly's Meerkat, Syndic8, the Google API and numerous other applications. For more information about XML-RPC, be sure to visit the official website at http://www.xmlrpc.com or read Programming Web Services with XML-RPC.

NuSOAP and PHP

SOAP is designed as an XML wrapper for web services requests and responses. SOAP's strength lies in its use of namespaces, XML Schema datatypes, and its flexibility with regard to transports. The disadvantage of SOAP is the fact that the specs and implementation is more complex, especially when you compare it to the simple XML-RPC approach. SOAP is the bread and butter of corporate web service developers. It has been deeply integrated into Microsoft .NET and IBM WebSphere. Based on its popularity, Google and Amazon.com have both created SOAP-based Web services.

NuSOAP is a powerful API developed for the PHP platform. It allows you to build both Web service clients and servers. One of the great features of NuSOAP is the built-in WSDL support. Installing the API is a snap: all you need is a PHP enabled server. The required libraries are contained in a file called nusoap.php. You can download both the toolkit and the documentation at this link: http://deitrich.ganx4.com/nusoap/.

Creating a NuSOAP Client using PHP

Creating a SOAP client with the library is very simple. To illustrate how the SOAP client works, I've decided to call a web service from XMethods.net (http://www.xmethods.net). This website has a wide range of useful web services you can access freely. In our particular example, we will be querying the currency converter Web service to find out the exchange rate between Canadian and American dollars.

Here is a breakdown of the PHP code needed to access the currency converter. First, we need to bring in the library file. The require_once command will make sure the functions are only added once into our script.


<?

require_once('nusoap.php'); 

Next step we will define where the WSDL is located and create an instance of the soapclient class to access the web service. WSDL stands for Web Services Description Language, an XML file which describes the interface of a web service:


$wsdl="http://www.xmethods.net/sd/2001/CurrencyExchangeService.wsdl";

$client=new soapclient($wsdl, 'wsdl');

We then send in two parameters through the SOAP client, the two countries that we want to compare to get the currency exchange rate. We then call the getRate function and pass our parameters to get a response from the remote server. The result is then displayed to the user:

 

$param=array(

'country1'=>'usa',

'country2'=>'canada'

); 

echo $client->call('getRate', $param);

?>

To help better explain the process happening during SOAP communication, here is a sample of packet data transmitted from client to server and vice versa. First, our web client requests the WSDL file from the XMethods server:


GET /sd/2001/CurrencyExchangeService.wsdl HTTP/1.0

Host: www.xmethods.net

The WSDL file is then passed into our SOAP client. It contains definitions for all the methods available from the CurrencyExchangeService Web service:


HTTP/1.1 200 OK

Date: Tue, 10 Feb 2004 11:45:24 GMT

Server: Apache/1.3.26 (Unix) Enhydra-Director/3 PHP/4.0.6 DAV/1.0.3 AuthNuSphere/1.0.0

Last-Modified: Tue, 23 Oct 2001 18:10:37 GMT

ETag: "1981a8-654-3bd5b29d"

Accept-Ranges: bytes

Content-Length: 1620

Connection: close

Content-Type: text/xml



<?xml version="1.0"?>

<definitions name="CurrencyExchangeService" 

             targetNamespace="http://www.xmethods.net/sd/CurrencyExchangeService.wsdl" 

             xmlns:tns="http://www.xmethods.net/sd/CurrencyExchangeService.wsdl" 

             xmlns:xsd="http://www.w3.org/2001/XMLSchema" 

             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 

             xmlns="http://schemas.xmlsoap.org/wsdl/">

 <message name="getRateRequest">

  <part name="country1" type="xsd:string"/>

  <part name="country2" type="xsd:string"/>

 </message>

 <message name="getRateResponse">

  <part name="Result" type="xsd:float"/>

 </message>

 <portType name="CurrencyExchangePortType">

  <operation name="getRate">

   <input message="tns:getRateRequest" />

   <output message="tns:getRateResponse" />

  </operation>

 </portType>

 <binding name="CurrencyExchangeBinding" type="tns:CurrencyExchangePortType">

  <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>

  <operation name="getRate">

   <soap:operation soapAction=""/>

   <input >

    <soap:body use="encoded" namespace="urn:xmethods-CurrencyExchange" 

      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   </input>

   <output>

    <soap:body use="encoded" namespace="urn:xmethods-CurrencyExchange"

      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>

   </output>

  </operation>

 </binding>

 <service name="CurrencyExchangeService">

  <documentation>Returns the exchange rate between the two currencies</documentation>

  <port name="CurrencyExchangePort" binding="tns:CurrencyExchangeBinding">

   <soap:address location="http://services.xmethods.net:80/soap"/>

  </port>

 </service>

</definitions>

Next step, our client contacts the server requesting the conversion rate using the getRate method. The parameters are sent according to the Web service specifications:


POST /soap HTTP/1.0

User-Agent: NuSOAP v0.6

Host: services.xmethods.net

Content-Type: text/xml

Content-Length: 622

SOAPAction: ""



<?xml version="1.0"?>

<SOAP-ENV:Envelope SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"

  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:si="http://soapinterop.org/xsd"

  xmlns:galactivism="urn:xmethods-CurrencyExchange">

<SOAP-ENV:Body>

<galactivism:getRate>

<country1 xsi:type="xsd:string">usa</country1>

<country2 xsi:type="xsd:string">canada</country2>

</galactivism:getRate>

</SOAP-ENV:Body>

</SOAP-ENV:Envelope>

Finally, the web service responds with the result of our query wrapped in a SOAP message. The result of the exchange rate between Canada and the US is 1.3267. The result is displayed on the screen without the SOAP headers.


HTTP/1.1 200 OK

Date: Tue, 10 Feb 2004 11:45:27 GMT

Server: Electric/1.0

Content-Type: text/xml

Content-Length: 492

X-Cache: MISS from www.xmethods.net

Connection: close



<?xml version='1.0' encoding='UTF-8'?>

<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' 

      xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 

      xmlns:xsd='http://www.w3.org/2001/XMLSchema' 

      xmlns:soapenc='http://schemas.xmlsoap.org/soap/encoding/' 

      soap:encodingStyle='http://schemas.xmlsoap.org/soap/encoding/'>

<soap:Body>

<n:getRateResponse xmlns:n='urn:xmethods-CurrencyExchange'>

<Result xsi:type='xsd:float'>1.3267</Result></n:getRateResponse>

</soap:Body>

</soap:Envelope>

Creating a NuSOAP Web service

The next example will demonstrate how to create a web service using NuSOAP. One of the nice features of NuSOAP is the web service summary information and the dynamically generated WSDL files. Here is an example of a web service summary:

We will now create a web service called CanadaTaxCalculator which will calculate the Provincial & Federal sales tax for Ontario Canada just like the XML-RPC example. Let's look at the code:


<?

require_once("nusoap.php");

$ns="http://localhost/nusoap";

First we must include our NuSOAP library and define the namespace of the service. It is usually recommended that you designate a distinctive URI for each one of your Web services.


$server = new soap_server();

$server->configureWSDL('CanadaTaxCalculator',$ns);

$server->wsdl->schemaTargetNamespace=$ns;

Next step, we instantiate the SOAP server and define the settings for our WSDL file such as the service name and the namespace.


$server->register('CalculateOntarioTax',

array('amount' => 'xsd:string'),

array('return' => 'xsd:string'),

$ns);

Then we register our PHP tax calculation function. This step will make the server "aware" of the existence of the CalculateOntarioTax method and the values that will pass to and from the method. If you have several methods, you must register each one separately.


function CalculateOntarioTax($amount){

$taxcalc=$amount*.15;

return new soapval('return','string',$taxcalc);

}

Then we invoke the service using the following line of code:


$server->service($HTTP_RAW_POST_DATA);

?>

That's it. Save your code in a file called "server.php", place the file on your web server and voila! You can now view dynamic WSDL formatted data by typing the following URL (be sure to substitute "localhost" with your own server name):

http://localhost/server.php?wsdl

In fact, appending "?wsdl" to the end of any PHP NuSOAP server file will dynamically produce WSDL code. Here's how our CanadaTaxCalculator Web service is described using 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:si="http://soapinterop.org/xsd" 

                 xmlns:tns="http://localhost/nusoap" 

                 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 

                 xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 

                 xmlns="http://schemas.xmlsoap.org/wsdl/" 

                 targetNamespace="http://localhost/nusoap">

     <types>

      <xsd:schema targetNamespace="http://localhost/nusoap">

        <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> 

        <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /> 

      </xsd:schema>

  </types>

  <message name="CalculateOntarioTaxRequest">

    <part name="amount" type="xsd:string" /> 

  </message>

  <message name="CalculateOntarioTaxResponse">

    <part name="return" type="xsd:string" /> 

  </message>

  <portType name="CanadaTaxCalculatorPortType">

   <operation name="CalculateOntarioTax">

    <input message="tns:CalculateOntarioTaxRequest" /> 

    <output message="tns:CalculateOntarioTaxResponse" /> 

   </operation>

  </portType>

  <binding name="CanadaTaxCalculatorBinding" type="tns:CanadaTaxCalculatorPortType">

    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" /> 

  <operation name="CalculateOntarioTax">

    <soap:operation 

     soapAction="http://localhost/nusoap/onttaxws.php/CalculateOntarioTax" 

     style="rpc" /> 

      <input>

       <soap:body use="encoded" 

        namespace="http://localhost/nusoap" 

        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> 

      </input>

      <output>

       <soap:body use="encoded" 

        namespace="http://localhost/nusoap" 

        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" /> 

      </output>

  </operation>

  </binding>

  <service name="CanadaTaxCalculator">

   <port 

    name="CanadaTaxCalculatorPort" 

    binding="tns:CanadaTaxCalculatorBinding">

   <soap:address location="http://localhost/nusoap/onttaxws.php" /> 

  </port>

 </service>

</definitions>

Creating a NuSOAP Web Service Client

What if we want to consume the web service we just created? Not a problem at all. Let's build a client from scratch. First, we must instantiate the NuSOAP client object and pass the WSDL file with the relevant web service definitions into the client.


<?php

require_once('nusoap.php'); 

$wsdl="http://localhost/server.php?wsdl";

$client=new soapclient($wsdl, 'wsdl'); 

Next step, we create a set of parameters to pass into the Web service and then remotely call the CalculateOntarioTax method. The example shows us sending an amount of $15.00 into the web service to find out how much tax we would have to pay in Ontario.


$param=array(

'amount'=>'15.00',

); 

echo $client->call('CalculateOntarioTax', $param);

?>

REST

REST differs a great deal from SOAP and XML-RPC. First, it's not a standard, whether formal or informal. Second, there is no standardized toolkit or pre-built client and server classes in PHP. The strength of REST is that you won't need special extensions or tools to develop web services. The HTTP protocol specifications contains everything you would need to transmit and receive XML messages.

REST, which stands for "Representational State Transfer", is a simpler approach than XML-RPC or SOAP, using standard HTTP methods such as GET, POST and PUT to send and retrieve XML data. You can then use tools like PHP DOM, SAX, or even XSL to do the parsing. The downside is that everything has to be built by hand (though REST toolkits and frameworks are starting to appear in some languages). If you want to develop REST web services, you must thoroughly understand XML and HTTP. Expect to have to write a little more code yourself.

Consuming an XML Web service using REST

To pass XML data from one server to another, we will be using the cURL PHP extensions. The Client URL Request Library is a versatile set of classes that enable PHP to transmit and receive files via HTTP. The data that is transmitted is a simple XML file, therefore you are not required to create an elaborate server. No custom wrappers or APIs are needed either. REST is increasing in popularity: in fact, Amazon.com has created a popular web service designed around the REST concept.

Let's look at the code for our REST web service client. First, we define the file location and the name of the XML file; if you want to dynamically generate the XML file, all you need to do is design a custom PHP script that accepts parameters (through the query string), incorporates your processing functions and outputs an XML file with the "result":


<?

$rs="http://localhost/xmldata.xml";

$qs="";

Then we create an array which will contain the parameters we will pass in the query string. Each one of the parameters is then urlencoded so that it may pass without problems in the HTTP header:


$parray=array('amount'=>"15.00");

foreach($parray as $par=>$value){ 

$qs=$qs."$par=".urlencode($value)."&";}

$uri="$rs?$qs";

Then we instantiate the cURL object. The CURLOPT_RETURNTRANSFER parameter specifies that we want the data to transmit even if errors are encountered. We then execute the transfer and pass the resulting data into a variable called $xml:


$cobj=curl_init($uri);

curl_setopt($cobj,CURLOPT_RETURNTRANSFER,1);

$xml=curl_exec($cobj);

curl_close($cobj);

echo $xml;

?>

Here is the code for the REST server. The program grabs the dollar amount from the query string, executes the tax calculation then outputs XML with the result:


<?

$amount=$_GET["amount"];

$taxcalc=$amount*.15;

echo "<?xml version=\"1.0\"?>";

echo "<taxinfo>";

echo "<result>".$taxcalc."</result>";

echo "</taxinfo>";

?>

Conclusion

With three different methods of consuming and producing web services and tons of toolkits at your disposal, there is simply no reason why you can't use PHP to develop web based XML applications. Be sure to download our sample code and good luck.