PHPBuilder - Using XML, a PHP Developer's Primer: Part 5



RSS Twitter
Articles Php Functions

Using XML, a PHP Developer's Primer: Part 5

by: Adam Delves
|
August 22, 2008

In the previous article in this series I demonstrated how XML is used to make remote procedure calls with XML-RPC in PHP. This article will focus on SOAP and WSDL (both of which use XML as their underlaying method of describing data) and demonstrate how a PHP script can act as a SOAP client and auto-magically discover detailed information about a web service.

An overview of Soap
SOAP, originally an acronym for Simple Object Access Protocol, is a lightweight XML-based messaging protocol designed to work in a distributed environment. It provides a foundation layer which can be built upon to facilitate complex messaging among web services. SOAP messages are encoded using XML and XML-RPC messages, and they are often sent over an HTTP connection. A simple SOAP envelope (message) that asks for the time may look like this:

<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://shcemas.xmlsoap.org/soap/envelope/" >
    <soap:Body>
        <getTime xmlns="http://www.phpbuilder.com/adam_delves/fourth_dimension/">
            <timeZone>London/Europe</timeZone>
        </getTime>
    </soap:Body>
</soap:Envelope>


SOAP makes use of XML name spaces. The URI by convention should link to a schema document defining the name space. The soap name space contains the soap envelope and the default name space used in the getTime element contains the message. The response might look as follows:
<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope xmlns:soap="http://shcemas.xmlsoap.org/soap/envelope/" >
    <soap:Body>
        <getTimeResponse xmlns="http://www.phpbuilder.com/columns/adam_delves/fourth_dimension/">
            <time>
                <hour>12</hour>
                <minute>34</minute>
                <second>10</second>
            </time>
            <format>24h</format>
        </getTimeResponse>
    </soap:Body>
</soap:Envelope>


Remote Procedure Call Using Soap
One of the applications of SOAP is to describe the message patterns of RPC calls and responses between web services. The format of an RPC call contained in a SOAP envelope is slightly different and more complex than an XML-RPC message. However, its complexity allows for scalability in describing more complex service structures and data types.

A SOAP RPC Call
<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:s-enc="http://schemas.xmlsoap.org/soap/encoding/"
    soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding" >

    <soap:Body>
        <verify xmlns="http://www.phpbuilder.com/columns/adam_delves/email_validator.verify/">
            <symbol xsi:type="xsd:integer">54</symbol>
            <symbol xsi:type="xsd:string">RG5H4</symbol>
        </verify>
    </soap:Body>
</soap:Envelope>


A SOAP RPC Response
<?xml version="1.0" encoding="UTF-8" ?>
<soap:Envelope
    xmlns:soap="http://shcemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:s-enc="http://schemas.xmlsoap.org/soap/encoding/"
    soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding" >

    <soap:Body>
        <verifyResponse xmlns="urn:webservices-email_validator">
            <Result xsi:type="xsd:boolean">false</Reponse>
        </verifyResponse>
    </soap:Body>
</soap:Envelope>


In the above example, 4 additional namespaces are used.
  • http://shcemas.xmlsoap.org/soap/envelope/ – is the XML Schema language. This contains the basic data types.
  • http://www.w3.org/2001/XMLSchema – Used in xsi:type attributes to specify the data type.
  • http://schemas.xmlsoap.org/soap/encoding/ – SOAP specific type encodings.
  • http://www.phpbuilder.com/columns/adam_delves/email_validator.verify/ - this URI does not exist but serves to identify the name space for the verify method of the email validator
Soap-RPC and WSDL
On its own, an RPC called using SOAP looks like a bloated XML-RPC call. Using XML, RPC would be a lot less expensive, parsing wise, than using SOAP. Web Services Description Language (WSDL) serves to put SOAP-RPC into context. A WSDL file which compliments a web service describes all the operations and data types that the web service interface exposes. This is especially useful in object-orientated programming languages as it enables the client using the web service to automatically replicate any required objects, methods and data types and decode the SOAP responses appropriately.
WSDL is an XML-based language. The client initialises their web service with the URI or path of the WSDL file (which need not reside on the same server as the service endpoint) and generates proxy code on the fly.

Using Soap in PHP 5
As SOAP is not enabled by default in PHP, you may need to enable it before continuing. There are two main SOAP implementations available, PEAR-SOAP and PHP's own SOAP extension. Where it is available PHP's own extension should be used, as it is written in C, and is faster and more efficient than the PEAR implementation. In this article, the code samples will apply to PHP's SOAP extension.

Installation
PHP's SOAP extension is not enabled by default in PHP. It is however included as part of the source code and as a dll file in the Windows ZIP distribution. To check whether the SOAP extension is already enabled, create a file containing a call to phpinfo() and check for a section named SOAP.

Windows
To enable the SOAP extension on a Windows system you must add the following line to your php.ini configuration file in the extensions section and ensure that the file php_soap.dll is contained in the directory specified in the extension_dir setting:
extension=php_soap.dll

If the php_soap.dll file is not present on your system, download the latest Windows version of PHP distributed as a ZIP file from the PHP web site.

Unix
To enable SOAP support on a UNIX system, you must re-compile the PHP interpreter. The SOAP extension also requires a libxml-2.5.4 or greater, which can be obtained from XMLSoft, assuming none are present on your system. First download and install libxml, then recompile PHP.

# wget ftp://xmlsoft.org/libxml2/libxml2-2.6.11.tar.gz
# tar xzvf libxml2-2.6.11.tar.gz
# cd libxml2-2.6.11
# ./configure
# make
# su -c "make install"

# wget http://uk2.php.net/get/php-5.1.4.tar.gz/from/uk.php.net/mirror
# tar zxvf php-5.1.4.tar.gz
# cd php-5.1.4
# ./configure --enable-soap
# make
# su -c "make install"

Creating a SOAP Client in PHP
The easiest method of creating a Soap Client is to give it the URL or path of the WSDL file containing all the information on how to interact with the web service. At this point, the object is initalised with all the methods of the web service and can be called as functions of the soap client.
The following PHP code makes a request to one of the Amazon Web Services API's:

PHP:

$params->AWSAccessKeyId = AMAZON_API_KEY;
$params->Request->SearchIndex 'Books';
$params->Request->Keywords 'php5 oop';
   
$amazon = new SoapClient('http://webservices.amazon.com/AWSECommerceService/AWSECommerceService.wsdl');
$result $amazon->itemSearch($params);


Note: Before using this code you need to obtain your own API Key.

  • The Amazon web service API's require the parameters to be sent in the form of an object. This is initialized with all the appropriate values first.
  • First an instance of the SoapClient object is created using the URI of the WSDL file.
  • The itemSearch() operation which is now a method of the SoapClient object is called. This causes PHP to make an HTTP request to the web service, sending a SOAP envelope containing the RPC call.
  • The return value of the itemSearch operation sent back from the web service is returned by the itemSearch() method and is decoded into native PHP types automatically.
  • The result, like the parameter passed to the function, is in the form of an object. If the request is successful, the Item property will contain an array of Item objects for the items found.

Generating PHP Code using WSDL
When initialised with a WSDL file, the __getTypes() function of the SoapClient object returns useful information about the data types supported by the web service. Being an untyped language, most of this information is irrelevant to us. However, the object data types can still be replicated in PHP and mapped to the data types exposed by the web service.

Using the Proxy Generator Class
The proxy generator class creates and returns an instance of the SoapClient. However, prior to this it uses the __getTypes() function to create all the class objects and maps them in the classmap array passed in the SoapClient's constructor.

PHP wsdl_proxy_generator.php:


class 
WSDLProxyGenerator
{
       
    
/**
     * Creates an instance of a SoapClient object and all necessary suporting types.
     *
     */
    
public static function createSoapClient($wsdlUri$clientName='WsdlGerneratedWebService')
    {
        
/* create the SoapClient object using the wsdl file */
        
$soap = new SoapClient($wsdlUri);   
        
$types = array();
           
        foreach(
$soap->__getTypes() as $type) {
           
            
/* match the type information  using a regualr expession */
            
preg_match("/([a-z0-9_]+)\s+([a-z0-9_]+(\[\])?)(.*)?/si"$type$matches);

            
$type $matches[1];
               
            switch(
$type) {
                
/* if the data type is struct, we create a class with this name */
                
case 'struct':
                    
/* the PHP class name will be ClientName_WebServiceName */
                    
$className $clientName '_' $name;
                       
                    
/* store the data type information in an array for later use in the classmap */
                    
$types[$name] = $className;
                       

                    
/* check the class does not exsits before creating it */
                    
if (! class_exists($className)) {
                        eval(
"class $className {}");
                    }
                       
                    break;
            }
        }

        
/* create another instance of the SoapClient, this time using the classmap */           
        
return new SoapClient($wsdlUri, array('classmap' => $types));
    }
       
    
/* this class cannot be instantiated as an object */
    
private function __construct()
    {           
    }
               
}


Comment and Contribute

Your comment has been submitted and is pending approval.

Author:
Adam Delves

Comment:



Comment:

(Maximum characters: 1200). You have characters left.