Developing Web Services Using PHP

From The Dumping Ground

Source

ONLamp.com    
Published on ONLamp.com (http://www.onlamp.com/)

by Deepak Vohra

07/26/2007

A web service consists of a server to serve requests to the web service and a client to invoke methods on the web service. The PHP class library provides the SOAP extension to develop SOAP servers and clients and the XML-RPC extension to create XML-RPC servers and clients. Before I delve further into developing web services with PHP, I shall briefly discuss web services.

Introduction to Web Services

A web service is a software system designed for interoperable interaction over a network. A web service is defined with a WSDL (Web Services Description Language) document, and other systems interact with the web service using SOAP messages, transferred using HTTP with an XML serialization. A web service is an abstract resource that provides a set of functions and is implemented by an agent, which sends and receives messages. A provider entity provides the functionality of a web service with a provider agent and a requester entity uses the web service functionality with a requester agent. Web services implement various technologies, some of which are XML, SOAP, and WSDL. XML is a standard format for data exchange. Web service requests and responses are sent as XML messages. The elements and attributes that may be specified in an XML document are specified in an XML Schema. SOAP provides a standard framework for packaging and exchanging XML messages. WSDL is an XML document in the http://schemas.xmlsoap.org/wsdl/ namespace for describing a web service as a set of endpoints operating on messages. A WSDL document specifies the operations (methods) provided by a web service and the format of the XML messages.

Installing the PHP Web Services Extensions

The SOAP and XML-RPC extensions are packaged with the PHP 5 installation. The SOAP extension and the XML-RPC extension are not enabled by default in a PHP installation. To enable the SOAP and XML-RPC extensions add the following extension directives in the php.ini configuration file.

extension=php_xmlrpc.dll
extension=php_soap.dll

Restart the Apache 2 server to activate the SOAP and XML-RPC extensions. The SOAP extension supports subsets of the SOAP 1.1, SOAP 1.2, and WSDL 1.1 specifications.

Creating a SOAP Web Service

After activating the SOAP extension in the PHP configuration file, a SOAP server and a SOAP client may be created using the SOAP PHP class library. A SOAP server serves web service requests and a SOAP client invokes methods on the SOAP web service. The SOAP library provides various functions for creating a SOAP server and a SOAP client. Some of the commonly used SOAP functions are discussed in Table 1.

Table 1. SOAP functions

Method Description
SoapServer->__construct( mixed wsdl [, array options] ) Creates a SoapServer object. The wsdl parameter specifies the URI of the WSDL. SoapServer options such as SOAP version may be specified in the options array.
SoapServer->addFunction( mixed functions ) Adds one or more PHP functions that will handle SOAP requests. A single function may be added as a string. More than one function may be added as an array.
SoapServer->fault() SoapServer fault indicating an error.
SoapServer->getFunctions() Returns a list of functions.
SoapServer->handle() Processes a SOAP request, invokes required functions and sends back a response.
SoapServer->setClass(string class_name [, mixed args [, mixed ...]] ) Sets the class that will handle SOAP requests. Exports all methods from the specified class. The args are used by the default class constructor.
SoapHeader->__construct() Creates a SOAP header.
SoapClient->__soapCall( string function_name, array arguments [, array options [, mixed input_headers [, array &output_headers]]] ) Invokes a SOAP function.
SoapClient->__doRequest() Performs a SOAP request.
SoapClient->__getFunctions() Returns a list of SOAP functions.
SoapClient->__getTypes() Returns a list of SOAP types.

Creating a SOAP Server

Before we create a SOAP server we need to create a WSDL document defining the web service. The WSDL document defines the operations that the web service provides. I will create an example web service that provides an operation getCatalogEntry, which returns a catalog entry for a catalog ID. A WSDL is an XML document in the http://schemas.xmlsoap.org/wsdl/ namespace. Some of the elements of a WSDL document are discussed in Table 2.

Table 2. WSDL elements

Element Description
definitions Root element of a WSDL document.
types Specifies data type definitions for the messages exchanged by the web service. XML Schema is the recommended type system.
message Defines the data being transmitted. A message consists of one or more parts. A part is associated with a type.
portType Defines a set of operations and the input-output messages for each operation.
operation An action (method) supported by the service. Each operation consists of input and output messages.
input Specifies a message format for the request.
output Specifies a message format for the response.
binding Defines message format and protocol details for operations and messages for a particular portType.
service Specifies a group of ports.
port Defines an endpoint by associating an address with a binding.

Next, create a WSDL document for the example web service. The example WSDL document, catalog.wsdl, defines message elements getCatalogRequest and getCatalogResponse for the request and response messages. In the WSDL document define a portType, CatalogPortType, for the getCatalogEntry operation that returns a catalog entry as a HTML string for a string catalogId. Define a binding, CatalogBinding, for the getCatalogEntry operation and the input output messages. The soap:binding element specifies that the binding is bound to the SOAP protocol format. The soap:operation element specifies information for the operation. The soap:body element specifies how the message parts appear inside the SOAP Body element. Define a service CatalogService that consists of a port, CatalogPort, which is associated with the CatalogBinding binding. The soap:address element specifies the URI of an address. The catalog.wsdl WSDL document is listed below.

<?xml version ='1.0' encoding ='UTF-8' ?> 
<definitions name='Catalog' 
  targetNamespace='http://example.org/catalog' 
  xmlns:tns=' http://example.org/catalog ' 
  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='getCatalogRequest'> 
  <part name='catalogId' type='xsd:string'/> 
</message> 
<message name='getCatalogResponse'> 
  <part name='Result' type='xsd:string'/> 
</message> 

<portType name='CatalogPortType'> 
  <operation name='getCatalogEntry'> 
    <input message='tns:getCatalogRequest'/> 
    <output message='tns:getCatalogResponse'/> 
  </operation> 
</portType> 

<binding name='CatalogBinding' type=
'tns:CatalogPortType'> 
  <soap:binding style='rpc' 
    transport='http://schemas.xmlsoap.org/soap/http'
  /> 
  <operation name='getCatalogEntry'> 
    <soap:operation soapAction='urn:localhost-catalog#
    getCatalogEntry'/> 
    <input> 
      <soap:body use='encoded' namespace=
      'urn:localhost-catalog' 
        encodingStyle='http://schemas.xmlsoap.org/soap
       /encoding/'/> 
    </input> 
    <output> 
      <soap:body use='encoded' namespace=
   'urn:localhost-catalog' 
        encodingStyle='http://schemas.xmlsoap.org/soap/
    encoding/'/> 
    </output> 
  </operation> 
</binding> 

<service name='CatalogService'> 
  <port name='CatalogPort' binding=
  'CatalogBinding'> 
    <soap:address location='http://localhost/
    soap-server.php'/> 
  </port> 
</service>
</definitions>

Copy the catalog.wsdl document to the C:\Apache2\htdocs directory, the directory in which PHP scripts are run. Create a PHP script, soap-server.php, to define the operations provided by the CatalogService web service. In the soap-server.php script define a function getCatalogEntry() that takes a catalogId as an argument and returns a string consisting of an HTML document. The HTML document string returned comprises of the catalog entry for the specified catalogId.

	
function getCatalogEntry($catalogId) { 
  if($catalogId=='catalog1')
          return "<HTML> … </HTML>";
elseif ($catalogId='catalog2')
return "<HTML>…</HTML>";
}

The WSDL cache is enabled by default. Disable the WSDL cache by setting the soap.wsdl_cache_enabled configuration option to 0.

ini_set("soap.wsdl_cache_enabled", "0");

Create a SoapServer object using the catalog.wsdl WSDL.

$server = new SoapServer("catalog.wsdl");

Add the getCatalogEntry function to the SoapServer object using the addFunction() method. The SOAP web service provides the getCatalogEntry operation.

$server->addFunction("getCatalogEntry");

Handle a SOAP request.

$server->handle();

The soap-server.php script is listed below.

<?php 
function getCatalogEntry($catalogId) { 
  if($catalogId=='catalog1')

return "<HTML>
 <HEAD>
  <TITLE>Catalog</TITLE>
 </HEAD
 <BODY>
<p> </p>
 <table border>
<tr><th>CatalogId</th>
<th>Journal</th><th>Section
</th><th>Edition</th><th>
Title</th><th>Author</th>
</tr><tr><td>catalog1</td>
<td>IBM developerWorks</td><td>
XML</td><td>October 2005</td>
<td>JAXP validation</td>
<td>Brett McLaughlin</td></tr>
</table>
</BODY>
</HTML>";

elseif ($catalogId='catalog2')

return "<HTML>
 <HEAD>
  <TITLE>Catalog</TITLE>
 </HEAD
 <BODY>
<p> </p>
 <table border>

<tr><th>CatalogId</th><th>
Journal</th><th>Section</th>
<th>Edition</th><th>Title
</th><th>Author
</th></tr><tr><td>catalog1
</td><td>IBM developerWorks</td>
<td>XML</td><td>July 2006</td>
<td>The Java XPath API
</td><td>Elliotte Harold</td>
</tr>
</table>
</BODY>
</HTML>";
} 

ini_set("soap.wsdl_cache_enabled", "0"); 
$server = new SoapServer("catalog.wsdl"); 
$server->addFunction("getCatalogEntry"); 
$server->handle(); 

?>

In the next section, I will create a SOAP client to send a request to the SOAP server.

Creating a SOAP Client

Create a PHP script, soap-client.php, in the C:\Apache2\htdocs directory. In the PHP script, create a SOAP client using the SoapClient class. The WSDL document, catalog.wsdl, is specified as an argument to the SoapClient constructor. The WSDL document specifies the operations that are available to the SOAP client.

$client = new SoapClient("catalog.wsdl");

Specify the catalogId for which a catalog entry is to be retrieved. Invoke the getCatalogEntry method of the SOAP web service.

$catalogId='catalog1';
$response = $client->getCatalogEntry($catalogId);

Output the response to the browser.

echo $response;

The soap-client.php script is listed below.

<?php  
  $client = new SoapClient("catalog.wsdl");
  $catalogId='catalog1';
  $response = $client->getCatalogEntry($catalogId);
  echo $response;
?>

Invoke the soap-client.php PHP script with the URL http://localhost/soap-client.php.The catalog entry for the catalog1 catalogId gets output as shown in Figure 1.

Invoking the SOAP client

Figure 1. Invoking the SOAP client


Creating an XML-RPC Web Service

XML-RPC is a specification and a set of implementations designed for applications to make remote procedure calls over the network. The remote procedure calls are made using HTTP as the transport and XML as the encoding.

Structure of an XML-RPC Request and Response

An XML-RPC message is an HTTP-POST request. The request body is in XML format. The request is sent to an XML-RPC server, which runs some business logic and returns a response in the form of XML. An example XML-RPC request is listed below.

POST /php/xmlrpc-server.php HTTP/1.0
User-Agent: Example Client
Host: localhost
Content-Type: text/xml
Content-length: 190

<?xml version="1.0"?>
<methodCall>
   <methodName>getCatalog</methodName>
   <params>
      <param>
         <value><string>catalog1
         </string></value>
      </param>
   </params>
</methodCall>

The URI in the header, /php/xmlrpc-server.php specifies the server URI to which the request is sent. The HTTP version is also specified. The User-Agent and Host are required to be specified. The Content-Type is text/xml and the Content-Length specifies the content length.

The request body is in XML with root element as methodCall. The methodCall element is required to contain a sub-element methodName which specifies the name of the method to be invoked as a string. If the XML-RPC request has parameters, the methodCall element contains sub-element params. The params element contains one or more param elements. Each of the param elements contains a value element. The param value may be specified as a string, a Boolean, a four-byte signed integer, double-precision signed, floating point number, date/time, or base-64 encoded binary. The sub-element of value in which a param value is specified is different for different value types. If a type is not specified the default type is string. The sub-elements for the value types are listed in Table 3.

Table 3. Value elements

Value Type Element
ASCII String <string>
Four-byte signed integer <i4> or <int>
Boolean <boolean>
Double-precision signed or floating point number. <double>
Date/time <dateTime.iso8601>
Base-64 encoded binary. <base64>

A param value may also be of type <struct>. A <struct> element consists of <member> elements. Each <member> element contains a <name> element and a <value> element. An example of struct value is listed below.

<struct>
   <member>
      <name>catalogId</name>
      <value><string>catalog1
      </string></value>
      </member>
   <member>
      <name>journal</name>
      <value><string>IBM developerWorks
      </string></value>
   </member>
</struct>

A value element in a member element may be of any of the param data types including struct. A param type may also be of type <array>. An <array> element consists of of a element, which consists of one or more <value> elements. An example of an <array> param value is listed below.

<array>
   <data>
      <value><i4>1</i4></value>
      <value><string>IBM developerWorks</string></value>
      <value>XML</value>
      <value><string>Introduction to dom4j</string></value>
      <value><string>Deepak Vohra</string></value>
   </data>
</array>

A <value> element in a element may consist of any of the data types including struct and array. The server response to an XML-RPC request is in the format of XML. An example response is listed below.

HTTP/1.1 200 OK
Connection: close
Content-Length: 190
Content-Type: text/xml
Date: 
Server: Example Server

<?xml version="1.0"?>
<methodResponse>
   <params>
      <param>
         <value><string>Introduction to SQLXML</string></value>
      </param>
   </params>
</methodResponse>

If an error has not occurred, the server response returns "200 OK." The Connection header specifies the state of the connection after the response is completed. For non-persistent connection the Connection header value is "close." The Content-Type is text/xml. The response body is in XML format with root element as methodResponse. The methodResponse element consists of a single <params> element, which consists of a single <param> element. The <param> element contains a single <value> element.

Instead of a <params> element a methodResponse element may also consists of a single <fault> element. The <fault> element contains a <value> element, which contains a <struct> element with two <member> elements faultCode of type integer and faultString of type string. An example of a XML-RPC server response with a <<fault> element is listed below.

HTTP/1.1 200 OK
Connection: close
Content-Length: 190
Content-Type: text/xml
Date: 
Server: Example Server

<?xml version="1.0"?>
<methodResponse>
   <fault>
      <value>
         <struct>
            <member>
               <name>faultCode</name>
               <value><int>4</int></value>
            </member>
            <member>
               <name>faultString</name>
               <value><string>No such Method.</string></value>
            </member>
         </struct>
      </value>
   </fault>
</methodResponse>

Creating an XML-RPC Server

The PHP XML-RPC extension is a PHP implementation of the XML-RPC specification. The XML-RPC PHP class library provides functions to create a XML-RPC server and invoke methods on the server. Some of the commonly used XML-RPC functions are discussed in Table 4.

Table 4. XML-RPC PHP functions

Function Description
xmlrpc_server_create () Creates an XML-RPC server
xmlrpc_encode_request ( string method, mixed params [, array output_options] ) Generates XML for a method request or response. Returns a string or FALSE on error.
xmlrpc_encode ( mixed value ) Generates XML for a PHP variable.
xmlrpc_decode_request ( string xml, string &method [, string encoding] ) Decodes XML into PHP. Returns an array.
xmlrpc_get_type ( mixed value ) Returns XML-RPC data types, for example "struct", "int", "string", "base64" for a PHP value.
xmlrpc_set_type ( string &value, string type ) Sets xmlrpc type, base64, or datetime for a PHP string value. Returns True or False on error.
xmlrpc_server_register_method ( resource server, string method_name, string function ) Registers PHP function to a web service method. The method_name value is the same as the value of the methodName element in the XML-RPC request.
xmlrpc_server_call_method ( resource server, string xml, mixed user_data [, array output_options] ) Parses XML request and invokes method. Returns result of method call. The user_data parameter specifies any application data for the method handler function. The output_options parameter specifies a hashed array of options for generating response XML. The following options may be specified. output_type: Specifies output data type; "php" or "xml". Default data type is "xml". If output type is "php" other values are ignored. verbosity: Specifies compactness of generated message.escaping: Specifies if and how to escape some characters.version: Specifies version of XML to use. Value may be "xmlrpc", "soap 1.1" and "simple". Version may also be set to "auto", which specifies to use the version the request came in. encoding: Specifies the encoding of the data. Default is "iso-8859-1".Example value of the output_options parameter is as follows.$output_options = array( "output_type" => "xml", "verbosity" => "no_white_space", "escaping" => array("markup", "non-ascii", "non-print"), "version" => "xmlrpc", "encoding" => "utf-8" );
xmlrpc_is_fault ( array arg ) Determines if an array value represents XML-RPC fault.
xmlrpc_server_destroy ( resource server ) Destroys a server resource.

Create a PHP script, xmlrpc-webservice.php, in the C:/Apache2/htdocs directory. In the PHP script, define a function, hello_func. Any function that is invoked by a client is required to take three parameters: the first parameter is the name of the XML-RPC method invoked. The second parameter is an array containing the parameters sent by the client. The third parameter is the application data sent in the user_data parameter of the xmlrpc_server_call_method() function. In the hello_func function, retrieve the first parameter, which is a name sent by the client, and output a Hello message.

function hello_func($method_name, $params, $app_data)
{
   $name = $params[0];
   return "Hello $name.";
}

Create an XML-RPC server using the xmlrpc_server_create() method.

$xmlrpc_server=xmlrpc_server_create();

If a server does not get created the xmlrpc_server_create method returns FALSE. Register the hello_func function with the server using the xmlrpc_server_register_method method. The first argument to the xmlrpc_server_register_method method is the XML-RPC server resource. The second argument is name of the method that is provided by the web service, which is the <methodName> element value in a XML-RPC request. The third argument is the PHP function to be registered with the server.

$registered=xmlrpc_server_register_method 
($xmlrpc_server, "hello", "hello_func" );

If the PHP function gets registered, the xmlrpc_server_register_method returns TRUE.

Creating an XML-RPC Client

Next, I shall create a XML-RPC client to send a request to the XML-RPC server. First, specify the XML string to be sent in the request.

$request_xml = <<< END
<?xml version="1.0"?>
<methodCall>
    <methodName>hello</methodName>
    <params>
      <param>
        <value>
          <string>Deepak</string>
        </value>
      </param>
    </params>
<methodCall>
END;

To escape XML in PHP, <<<END ….END; is used. An XML document demarker other END may also be used. The methodCall element specifies the web service method that is to be invoked. Invoke the web service method using the xmlrpc_server_call_method function. The first argument to the xmlrpc_server_call_method function is the server resource. The second argument is the string containing the XML-RPC request. The third argument is the application data that is sent to the third parameter of the method handler function.

$response=xmlrpc_server_call_method( $xmlrpc_server, 
$request_xml, , array(output_type => "xml"));

Output the XML-RPC response from the server.

print $response;

The PHP script, xmlrpc-webservice.php, is listed below.

<?php
function hello_func($method_name, $params, $app_data)
{
$name = $params[0];
return "Hello $name.";
}

$xmlrpc_server=xmlrpc_server_create();
$registered=xmlrpc_server_register_method 
($xmlrpc_server, "hello", "hello_func" );

$request_xml = <<< END
<?xml version="1.0"?>
<methodCall>
    <methodName>hello</methodName>
      <params>
        <param>
          <value>
            <string>Deepak</string>
          </value>
        </param>
      </params>
</methodCall>
END;
$response=xmlrpc_server_call_method( $xmlrpc_server, 
$request_xml, '', array(output_type => "xml"));
print $response;
?>

Copy the xmlrpc-webservice.php script to the C:/Apache2/htdocs directory. Invoke the PHP script with the URL http://localhost/xmlrpc-webservice.php. The response from the server gets output to the browser as shown in Figure 2.

Response From XML-RPC Web Service Figure 2. Response from XML-RPC web service

To demonstrate an error in the request, returned as a <fault> element in the response XML, make the request XML not valid XML. For example replace </methodCall> with <methodCall> in the $request_xml. Invoke the xmlrpc-webservice.php. The response from the server is returned as a <fault> element that consists of a struct element value, which consists of faultCode and faultString members, as shown in Figure 3.

Response As fault Element Figure 3. Response as fault element

References

For more information, see W3C's WSDL page and SOAP Primer.

Deepak Vohra is a NuBean consultant and a web developer.

Copyright © 2009 O'Reilly Media, Inc.