#native_company# #native_desc#
#native_cta#

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

By Adam Delves
on 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="https://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="https://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="https://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.
  • https://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()

    {           

    }

               

}