Sr. Web Developer
mediabistro.com
US-NY-New York

Justtechjobs.com Post A Job | Post A Resume

Using XML: A PHP Developer's Primer, Part 4: XML-RPC, PHP and Javascript
XML-RPC
In this article we will demonstrate how PHP can be used to call upon web services provided by third part sites via an XML-RPC server. We will also show you how to create your own XML-RPC and use client-side Javascript to invoke procedures in your PHP scripts.

What is RPC?
RPC is an acronym for Remote Procedure Call. As the name suggests, RPC is a technology that provides an interface for making procedure calls over a network. Remote procedure calls are by no means a new technology. RPCs have been around since the emergence of computer networks and applications that communicate via these networks. Typically, two or more applications which need to communicate over a network use a protocol (a "communication language" of sorts). These protocols enable the two end points to send each other instructions and invoke certain procedures. HTTP, SQL and POP3 are all examples of RPC-like protocols.

The emergence of the Internet has made more applications network-aware and has thrown them all into the same domain. The mish mash of different protocols that are used by the ever-growing number of applications has made the task of creating new applications daunting for developers. The developers now have to familiarize themselves with several different protocols. RPCs address this problem by exposing a standard, generalized interface by which a method residing at a remote endpoint can be invoked, and its return value received like any other native procedure from within the program code. The developer need not worry about how and in what format the data is sent between the two endpoints. In some cases they may be unaware that they are even using an RPC!

The XML-RPC Payload
XML-RPC is an RPC protocol which uses XML mark-up to describe method calls and responses, making it ideal for use over the Internet. In most cases, HTTP is used as the transport medium for method calls and their responses. The XML request is sent in the body of an HTTP POST request and the response from the server is returned in the body of the HTTP response.

Calling An RPC
An RPC call needs to contain three pieces of information. The method name, its parameters (if any) and the data type of each parameter:

<?xml version="1.0" encoding="UTF-8" ?>
<methodCall>
    <methodName>emailValidator.verify</methodName>
    <params>
        <param><value><int>54</int></param>
        <param><value><string>RG5H4</string></param>
    </params>
</methodCall>


The Response
Upon successful execution of the RPC, the response to the above call would look something like this:

<?xml version="1.0" encoding="UTF-8" ?>
<methodResponse>
    <params>
        <param><value><boolean>1</boolean></param>
    </params>
</methodResponse>

If for any reason the method is not executed, the RPC server will return a fault response. The fault response contains a fault code and description in the following format:

<?xml version="1.0" encoding="UTF-8" ?>
<methodResponse>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>faultCode</name>
                        <value><int>1</int></value>
                    </member>
                    <member>
                        <name>faultString</name>
                        <value>Not enough parameters.</value>
                    </member>
                </struct>
            </vlaue>
        </param>
    </params>
</methodResponse>
A detailed XML-RPC specification can be found here.
XML-RPC and Objects
XML-RPC alone does not allow for the accurate implementation of object orientated method calls. Although the serialized data from an object (i.e., all its properties) can be encoded into a struct, there is no facility to describe the type and the operations provided by the object, such as a class definition. It is therefore up to the two end points (client and server) to handle the data in the correct context. As the ethos behind RPC was standardization and generalization, sending serialized objects via RPC is not recommended for several reasons:
  • Both endpoints must respect the visibility of the object's members (e.g: public, private, protected).
  • After each operation the data will need to be revalidated to ensure that it respects any constraints.
  • Serializing and unserializing an object is a resource intensive task
  • As a single operation typically affects only a single object property, much of the serialized data is redundant and gets passed back and forth unnecessarily.
The solution to this problem is to keep the object instance on the server and transpose only its public operations to RPCs. It is up to the server to keep track of and serialize individual object instances. For example, a customer object may be transposed as follows:

Customer Object

RPC Methods mapped to customer object:

Customer.getAddress(id)
Customer.getName(id)
Customer.setName(id, newName)
Customer.find(pattern)

The RPC client only needs to work with one piece of data: the customer ID. This is passed along with each RPC call and serves to uniquely identify the object instance. Although the data still needs to be serialized, this is now only the servers job and it retains complete control over all the data that is connected with the customer.

Another solution is to use SOAP (Simple Object Access Protocol), an application of RPC and WSDL (Web Services Description Language). It provides a robust way to work with remote object instances. This will be explored in more detail in the next article in this series.
XML-RPC in PHP
The PEAR XML-RPC package is included with PEAR in the standard PHP installation. It provides both an XML-RPC client and server. I will explain first how we go about calling RPCs from PHP.

More and more websites are making their services available via RPC. This promotes the sharing of information and the re-usability of existing services to enrich the content that web developers can provide. For example, a typical web application may call on news services, search engines and online payment services to provide the functionality that would otherwise require code written by in-house developers.

In the following example I will demonstrate how to use the PEAR RPC client in PHP to call several APIs provided by the online photo sharing service Flickr. The example will display the four most recent public pictures added to a specified users Flickr account.

Flickr

Flickr is a new online photo-based service which allows its users to upload, share, tag and add comments to their pictures as well as order physical reprints. It also provides a web service API, which is made accessible via RPC. The API allows the developer to do anything a normal user can do but via the API. If you wish to access the API, you first need to sign up for an account and obtain an API Key, this needs to be passed as an argument to each RPC called on Flickr.

The API's provided by Flickr typically expose multiple optional arguments. For this reason each RPC accepts a single parameter containing a structure, each member of which is an argument that is to be passed to the RPC. This enables the developer to send only the parameters which are needed in any order:

<struct>
    <member>
        <name>apiKey</name>
        <value><string>123456</string></value>
    </member>
    <member>
        <name>userName</name>
        <value><string>visualAd</string></value>
    </member>
</struct>
The method response is always returned as a string in the form of an XML document containing the data or response to the operation requested. The documentation for each API describes the format of the XML document returned. We can use the tools provided to us by PHP to interpret this XML response.

Using the PHP XML-RPC Client
PEAR is included by default with PHP. Provided the interpreter was not compiled with the -–without-pear configuration option, the PEAR XML-RPC package will be available by default. To include the RPC client in your scripts, use the following line:

require_once 'XML/RPC.php';

The XML-RPC client provides four objects:
  • XML_RPC_Client - provides the transport by which RPC calls are sent and RPC responses received.
  • XML_RPC_Message – used to store a RPC call and its parameters
  • XML_RPC_Value – Represents an XML-RPC value and its data type.
  • XML_RPC_Response – Holds an XML-RPC response.

The PHP application will take a username variable as input from the query string. The script will call two Flickr APIs. The first flickr.people.findByUsername, returns the unique user ID for the user given in the query string. The second API, flickr.people.getPublicPhotos, returns a list of public photos attached to the user ID.
Finding the User ID
A function wrapper is used to call the flickr.people.findByUsername and return the required data. To aid with readability, the wrapper function is given the same name as the API function that is being called.
  • The function first uses the arguments that are passed to it to set up the parameters which will be passed to the RPC message. An associative array is used to store each named argument expected by the API. When passed to an XML_RPC_Value object, an associative array is converted to an XML-RPC structure.

    new XML_RPC_Value($params'struct')

  • When initializing an XML_RPC_Value object, the second optional argument can be used to set the data type. If omitted, it defaults to a string.

  • The XML_RPC_Message object expects the name of the RPC and an array of XML_RPC_Value objects, which will be passed as parameters. When initializing an XML_RPC_Message object, the @ error suppression operator is used to prevent PHP from spitting out any notices.

    @(new XML_RPC_Message('flickr.people.findByUsername',
                                array(new 
    XML_RPC_Value($params'struct'))));

  • The send method of the XML_RPC_Client object sends the XML-RPC message to the server and returns the response. 
  • Upon receiving the response, the faultCode() method is called. A non-zero value indicates that a fault has occurred. If the RPC call fails, the fault description is loaded into the $error variable and a value of false is returned.

  • If the RPC call is successful, the return value (an XML document) is loaded into a Simple XML object and the value of the nsid attribute, as described in the API's documentation is returned.

PHP:

function 
flickr_people_findByUsername($username, &$error=false)
{
    
$apiKey $GLOBALS['apiKey']; // holds a reference to the RPC Client
    
$flickr $GLOBALS['flickr']; // an XML_RPC_Value object containing the API key

    
$params['api_key'] = $apiKey;
    
$params['username'] = new XML_RPC_Value($username);

    
/* create an RPC Message and initialize it with the parameters */
    
$flickr_people_findByUsername = @(new XML_RPC_Message('flickr.people.findByUsername',
                            array(new 
XML_RPC_Value($params'struct'))));

    
/* call the RPC */
    
$response $flickr->send($flickr_people_findByUsername);
    
    
/* check for a fault */
    
if (! $response->faultCode()) {
        
/* load the response into a Simple XML object */
        
$xml simplexml_load_string(XML_RPC_Decode($response->value()));
        
        return 
$xml['nsid']; // return the user ID
    
} else {
        
$error $response->faultString(); // set the $error variable to the fault description
        
return false;
    }
}
Returning a list of Photos

The flickr.people.getPublicPhotos function returns a list of photos in XML format. Each photo element has contains the information required to construct a URL to retrieve the image from the Flickr server.
  • Again, the wrapper function uses the arguments passed to construct an associative array of arguments to pass to the RPC function.
  • A SimpleXMLElment object is used again to represent the data returned in the response.
  • If no photos are returned, the function returns false.
  • If one or more photos were found, the function returns the list of photos as a SimpleXMLElement object.

    return $xml->photo// return only the photo list

PHP:

function 
flickr_people_getPublicPhotos($user_id$per_page=null$page_no=null$extras=null, &$error=false)
{
    
$apiKey $GLOBALS['apiKey']; // holds a reference to the RPC Client
    
$flickr $GLOBALS['flickr']; // an XML_RPC_Value object containing the API key
    
    
$params['api_key'] = $apiKey;

    
$params['user_id'] = new XML_RPC_Value($user_id);
    
    
/* load optional arguments where they have been supplied */
    
if (! is_null($per_page)) {
        
$params['per_page'] = new XML_RPC_Value($per_page);
    }

    if (! 
is_null($page_no)) {
        
$params['page_no'] = new XML_RPC_Value($page_no);
    }

    if (! 
is_null($extras)) {
        
$params['extras'] = new XML_RPC_Value($extras);
    }    
    
    
/* create an RPC Message and initialize it with the parameters */
    
$flickr_people_getPublicPhotos = @(new XML_RPC_Message('flickr.people.getPublicPhotos',
                                array(new 
XML_RPC_Value($params'struct'))));        
    
    
/* call the RPC */
    
$response $flickr->send($flickr_people_getPublicPhotos);

    if (! 
$response->faultCode()) {
        
/* if the response is alid, load it into a SimpleXMLElement and return */
        
$xml simplexml_load_string(XML_RPC_Decode($response->value()));
    } else {
       
$error $response->faultString();
       return 
false;
    }    

    if (
$xml['total'] == 0) {
        
/* if not photos were found, return an error */
        
$error 'No Photos';
        return 
false;
    } else {
        return 
$xml->photo// return only the photo list
    
}
}
Displaying the Pictures
The main part of the script does the following:
  • Checks for the username variable in the query string.
  • Creates a global instance of an XML_RPC_Client object, which the two wrapper functions will user.
  • Creates a global instance of an XML_RPC_Value object, containing the API key, require by all Flickr API's.
  • Calls the two wrapper functions.
  • Displays an error, or the returned images by traversing through the photo elements in the SimpleXMLElement object.

PHP:

    $error false;
    
$apiKey = new XML_RPC_Value('[ENTER API KEY HERE]');
   
    
/* check first for a user name sent with the query string - if this does not exist an error is returned */
    
if (isset($_GET['username'])) {
   
    /* create the client - the URI of the server endpoint and the host are both required */
        
$flickr = new XML_RPC_Client('/services/xmlrpc/''www.flickr.com');
    
       
/* find the user_id */
        
$username $_GET['username'];
    
        
/* only get the pictures if the request for the user ID succeeded */
       
if (($user_id flickr_people_findByUsername($username$error))) {
            
$photos flickr_people_getPublicPhotos($user_id41null$error);   
        }    
    } else {
        
$error ='No Username Specified';
    }
Modifying the Email Validator
The real power of RPC can only be appreciated when it is used to bridge the gap between two different programming languages. The next example is going to convert the Ajax Email Validation application, as seen in the second article in this series, to call the RPCs exposed by a server endpoint written in PHP. The email validation application shows how Ajax can be used to validate an email address without submitting the form. The validator contains an Ajax engine which communicates with a back-end script.

The back-end script will now act as a RPC server and map several functions of the EmailValidator object to RPC. The Ajax engine will no longer be an Ajax engine (the Javascript RPC client will handle this); instead it will call the RPC functions that reside on the PHP server.

Before continuing, you'll need download the ZIP file that accompanies this article. As well as all the source code examples given here, it also contains additional components which will be used in this application:
  • emailValidator.php – This contains an Email Validation class that uses an SQLLite database to store verified email addresses and provides the methods necessary to check the email address syntax and verify the verification code.
  • ajrpc.js – This contains an XML-RPC client for Javascript. The AJ-RPC client uses the XMLHTTPRequest to send and receive the XML-RPC payloads and allows method RPC calls to be made both synchronously and asynchronously.
Continued Next Week!
Join us next week as we finish up Part 5 and discuss the creation of the PHP RPC server and interface!!