You can create a compliant XML-RPC client using a combination of the functions introduced earlier. This would require building a complete request and would require some additional work. The extension, however, provides a quick and simple function, allowing you to create a client—at least building the request structure—in as little as a single line of code. As previ- ously mentioned, the extension does not provide a mechanism for the data transport, so you will use the call_using_sockets()function from Listing 16-1 instead.
Using the xmlrpc_encode_request() Function
Calling the xmlrpc_encode_request()function with the appropriate arguments will create and return a complete XML-RPC request document that includes the methodCalland methodName information:
string xmlrpc_encode_request(string method, mixed params [, array output_options]) The methodparameter is simply the name of the method to be called on the remote server. The value passed to this parameter becomes the content of the methodNameelement in the request. The paramsparameter contains the PHP data that will be passed as the parame- ters to the server. If you have ever played around with this extension, you might have become a bit confused by the data passed to this argument.
Passing any scalar type, such as an integer or string or even an object, to this parameter creates a single paramelement for the data. Passing a numerically indexed array, on the other hand, creates a paramelement for each item in the array. So, to create a request that consists of a single parameter containing an array, the array needs to be passed as an item of an array:
echo xmlrpc_encode_request('remoteMethod', array(1, 2));
At first glance, you might think that a request containing a single parameter, which is an array, would be created. The resulting request structure, however, proves this to be wrong:
<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>mymethod</methodName>
<params>
<param>
<value><int>1</int></value>
</param>
<param>
<value><int>2</int></value>
</param>
</params>
</methodCall>
In fact, to pass the array as a single parameter, you need to send it as the only item within an encapsulating array:
echo xmlrpc_encode_request('remoteMethod', array(array(1, 2)));
Here you can see that to send array(1, 2)as a single parameter, you need to pass it within an array as array(array(1, 2):.
<?xml version="1.0" encoding="iso-8859-1"?>
<methodCall>
<methodName>remoteMethod</methodName>
<params>
<param>
<value>
<array>
<data>
<value><int>1</int></value>
<value><int>2</int></value>
</data>
</array>
</value>
</param>
</params>
</methodCall>
The last optional parameter, output_options, is an associative array containing any options that you may want to pass. The following are the possible options:
verbosity: Determines how the resulting XML is formatted. Available values are no_white_space, newlines_only, and pretty, which is the default.
escaping: Determines how and if certain characters are escaped. The value for this option can be one or many values. When setting multiple values, you pass them as an array. The possible values for this option are cdata, non-ascii, non-print, and markup.
The default for this option is the combination of non-ascii, non-print, and markup.
version: Specifies the format of the XML document. Possible values are xmlrpc, soap 1.1, and simple. The keyword autois also recognized and will create a response in the same format a request came in. The default for this option is auto(when applicable) and xmlrpc. (This chapter does not use this option, and all data is in xmlrpcformat.) encoding: The encoding for the XML document. The default encoding is iso-8859-1.
Decoding the Response
A client decodes responses it receives from the server using the xmlrpc_decode()function described in the earlier “Encoding and Decoding Data” section. The response can either con- tain any return values or contain a fault structure. As you will see in the next section, the fault structure allows the client to perform error handling when the server encounters an error try- ing to fulfill the request. For example:
$phpdata = xmlrpc_decode($retval);
Handling Errors
Whether or not the server produces an error when a request is made, a response is returned in XML format. After decoding the response, a client is able to determine whether the response is an error using the xmlrpc_is_fault()function. This function takes the return value from the xmlrpc_decode()function and returns TRUEif an error condition is detected and FALSEif no errors occurred from the request. For example:
$phpdata = xmlrpc_decode($retval);
/* check for errors */
if (xmlrpc_is_fault($phpdata)) {
print "Error Code: ".$phpdata['faultCode']."\n";
print "Error Message: ".$phpdata['faultString']."\n";
} else {
/* Process return value */
}
When a fault is detected, the resulting data is an array containing faultCodeand faultStringmembers. The actual structure of the XML document for a fault will be shown in the “Creating an XML-RPC Server” section. For now it is enough to understand that the faultCodeis an intdata type and that the faultStringis a stringdata type. These are the only two members allowed within a fault structure. The faultCodeis application dependant.
The XML-RPC specification does not have any predefined codes, so if you are handling errors based on the code, it is necessary that the creator of the XML-RPC server being accessed pro- vides the list of codes that can be returned.
Seeing an XML-RPC Client in Action
I will show a real example of an XML-RPC client later in this chapter in the “Seeing Some Examples in Action” section. However, that section shows only a client, so here, in order to explain both sides of the equation, I will show how to create a client that makes stock trades that are executed on a remote server. I explain and demonstrate the server in the “Creating an XML-RPC Server” section.
I wrote the following example to be executed from the command line using PHP CLI. With a little HTML and a few <BR>tags, it is quite easy to adapt it to run under a Web server. I wrote the server, created in the next section, to be executed from within a Web server. If you decide to try this code, make sure you add the call_using_sockets()function from Listing 16-1, or the equivalent function, to the script. In addition, make sure to set the remote server, sent as the first parameter, accordingly. Currently, the script assumes localhostrunning on port 80 and assumes the script being called is named stocktrader.phpand lives in the document root of the Web site. Here’s the code:
<?php
/* An array to hold data returned from server */
$arMessage = array();
/* The userid is obtained through some other mechanism */
$userid = 1;
/* Common function to make XML-RPC requests */
function make_request($request_xml, &$arMessage, $stockSymbol,
$stockQuantity, $transtype) {
$retval = call_using_sockets('localhost', 80, '/stocktrader.php', $request_xml);
$data = xmlrpc_decode($retval);
if (is_array($data) && xmlrpc_is_fault($data)) {
$arMessage[] = "Unable to $transtype $stockQuantity shares of $stockSymbol";
$arMessage[] = "Error Code: ".$data['faultCode'];
$arMessage[] = "Error Message: ".$data['faultString'];
} else {
$arMessage[] = $data;
} }
/* Stock symbol, quantity, and type of transaction (buy/sell) are obtained through some mechanism such as an HTML form */
/* Purchase 100 shares of Yahoo */
$stockSymbol = "YHOO";
$stockQuantity = 100;
$request_xml = xmlrpc_encode_request('stockPurchase', array($userid, $stockSymbol,
$stockQuantity));
make_request($request_xml, $arMessage, $stockSymbol, $stockQuantity, 'Purchase');
/* Add an blank to the message array to add extra line feed during output */
$arMessage[] = "";
/* Sell 50 shares of Google */
$stockSymbol = "GOOG";
$stockQuantity = 50;
$request_xml = xmlrpc_encode_request('stockSale', array($userid, $stockSymbol,
$stockQuantity));
make_request($request_xml, $arMessage, $stockSymbol, $stockQuantity, 'Sell');
/* Add an blank to the message array to add extra line feed during output */
$arMessage[] = "";
/* Buy 10 shares of Microsoft */
$stockSymbol = "MSFT";
$stockQuantity = 50;
$request_xml = xmlrpc_encode_request('stockPurchase', array($userid, $stockSymbol,
$stockQuantity));
make_request($request_xml, $arMessage, $stockSymbol, $stockQuantity, 'Purchase');
/* Output the messages received from the server */
foreach ($arMessage AS $message) { print $message."\n";
}
?>
This sample code begins with some initialization. First, the $arMessageand $useridvariables are set up. The $arMessagevariable is an array that will hold any return data or error information from the remote server. This will be used at the end of the script to output all the messages. The
$useridvariable is given the value 1. This variable represents the user ID for the person or entity making the stock transaction and is hard-coded for this example.
Second, the make_request()function is a common function that will send an encoded request to the server using the call_using_sockets()function from Listing 16-1. It takes five parameters. The first is the encoded XML-RPC request to be sent. The second is the $arMessage array passed by reference so that messages can be added and eventually used elsewhere in the script. The remaining three parameters strictly add some information in the event the server returns an error. As you can see within the error handling within the function, their text values are simply inserted into the error messages being created.
Once the request has been made, the returned data is then decoded and tested for an error. A fault structure is decoded into an array, and because a server can return any data type, the type must first be tested for an array prior to checking whether it is a fault. In the event the server returned an error, the error is dissected, and the error information is placed into the
$arMessagearray. In all other cases, the actual decoded data is placed directly in the array.
For the actual client calls, you can see in the code that three stock transactions are to be made. The first transaction attempts to purchase 100 shares of Yahoo stock. The second trans- action attempts to sell 50 shares of Google, and the last attempts to purchase 50 shares of Microsoft. They are all hard-coded for this example, but you could create a simple Web inter- face to allow for dynamic input.
The xmlrpc_encode_request()function creates the request document. As you can probably guess from looking at the function calls, the two methods being called on the remote server are stockPurchaseand stockSale. Each takes three parameters: the user ID, stock symbol, and stock quantity. The corresponding variables are simply passed as items within an array.
Once all requests have been made and the return values and/or errors are processed, the script simply loops through the $arMessagearray and outputs the messages. Although the server for this client has yet to be written, the following is the final output you would get once every- thing is assembled and the script ran:
Bought 100 shares of Yahoo!
Sold 50 shares of Google
Unable to Purchase 50 shares of MSFT Error Code: -1
Error Message: Stock Symbol MSFT cannot be traded
The first two messages are the values returned directly from the remote server. They show that the first two stock transactions were successfully executed. The last demonstrates a fault returned when trying to purchases 50 shares of Microsoft. To see how and why the return val- ues were created, you need to take a look at the server being used.