I l @ ve RuBoard |
An Alternative PHP/XML-RPC Implementation: XML-RPC for PHPBefore XML-RPC support appeared in PHP, Edd Dumbill's XML-RPC implementation was the only real choice for PHP developers working with the protocol. Powerful, flexible, and easy to use, this XML-RPC implementation provides developers with a full-featured alternative to the XML-RPC-EPI extension that first shipped with PHP 4.1.0. Released under the BSD license, it can be downloaded from http://phpxmlrpc. sourceforge .net/. Completely object-oriented in nature, Edd Dumbill's implementation begins with the construction of a fundamental unit: an XML-RPC value, or xmlrpcval , object. This object is then used as the basis for other more complex objects: an XML-RPC request, or xmlrpcmsg, object; and an XML-RPC response, or xmlrpcresp, object. Finally, client and server objects are available to handle data transmission between the two ends of the XML-RPC connection. In order to better understand this, consider Listing 8.9, which demonstrates the construction of an XML-RPC request using these objects.
Listing 8.9 Constructing an XML-RPC Request<?php // include the class definitions include("xmlrpc.inc"); /* create an instance of the xmlrpcmsg object the xmlrpcmsg object constructor requires two parameters: xmlrpcmsg(procedure, array of xmlrpcval objects) the xmlrpcval object constructor also requires two parameters: xmlrpcval(value, type) allowed xmlrpcval types are "i4", "int", "boolean", "double", "string", "dateTime.iso8601", "base64", "array", "struct"; */ $rpc = new xmlrpcmsg("getRandomQuote", array(new xmlrpcval("Churchill", "string"))); /* the serialize() method serializes the xmlrpcmsg object into an XML representation, suitable for transmission to an XML-RPC server */ echo $rpc->serialize(); ?> Listing 8.9 uses two objects: the fundamental xmlrpcval object for argument values; and the xmlrpcmsg object, which uses the xmlrpcval object in the construction of a complete XML-RPC request. Listing 8.10 demonstrates the output. Listing 8.10 An XML-RPC Request<?xml version="1.0"?> <methodCall> <methodName>getRandomQuote</methodName> <params> <param> <value> <string>Churchill</string> </value> </param> </params> </methodCall> Construction of an XML-RPC response is accomplished in a similar manner, except that this time an xmlrpcresp object is used. Listing 8.11 demonstrates how such a response might be constructed . Listing 8.11 Constructing an XML-RPC Response<?php // include class definitions include("xmlrpc.inc"); /* create an instance of the xmlrpcresp object the xmlrpcresp object constructor requires three parameters: xmlrpcresp(return value as xmlrpcval object, fault code, fault string) */ $rpc = new xmlrpcresp(new xmlrpcval("A fanatic is one who won't change his mind and won't change the subject", "string")); /* the serialize() method serializes the xmlrpcresp object into an XML representation, suitable for transmission to an XML-RPC client */ echo $rpc->serialize(); ?> Listing 8.12 demonstrates the output of Listing 8.11. Listing 8.12 An XML-RPC Response<methodResponse> <params> <param> <value> <string>A fanatic is one who won't change his mind and won't change the subject</string> </value> </param> </params> </methodResponse> With message construction out of the way, all that remains is the transmission layer for messages between the client and server. In this implementation, transmission is handled by two additional objects: xmlrpc_server and xmlrpc_client . They provide methods to send XML-RPC requests , receive XML-RPC responses, decode XML-RPC <value> s into native PHP data types, and log debug messages. In order to illustrate how this works, consider Listing 8.13 and Listing 8.14, which re-create the meteorological server and client from Listing 6.26 and Listing 6.27, again bypassing PHP's native XML-RPC extension in favor of this object-based implementation. Listing 8.13 demonstrates the code for the server. Listing 8.13 A Simple XML-RPC Server<?php // include class definitions include("xmlrpc.inc"); include("xmlrpcs.inc"); // create server object // the object constructor is used to map public procedure names to internal function names $server = new xmlrpc_server(array("getWeatherData" => array("function" => "phpWeather"))); // function to retrieve weather data // and return it in requested format (raw or average) function phpWeather($params) { // get reference to associative array passed to server by client $struct = $params->getParam(0); // get value of "city" key // the structmem() method returns a reference to a particular key of the array // the scalarval() method extracts the corresponding value $member = $struct->structmem("city"); $city = $member->scalarval(); // get value of "format" key $member = $struct->structmem("format"); $format = $member->scalarval(); // initialize error variables $error_msg = ""; $error_code = 0; if (!$city) { // no city code available // set error flag $error_msg = "Missing city code"; $error_code = 998; } else { // open connection to database $connection = mysql_connect("localhost", "rpc_agent", "secret") or $error = "Unable to connect to database"; mysql_select_db("weather_db") or $error = "Unable to select database"; // get data $query = "SELECT t1, t2, t3 FROM weather WHERE city = '" . $city . "'"; $result = mysql_query($query) or $error = "Error in query: $query. " .mysql_error(); // if a result is returned if (mysql_num_rows($result) > 0) { // get column values list($t1, $t2, $t3) = mysql_fetch_row($result); // close database connection mysql_close($connection); } else { // set error flag $error_msg = "No data available for that city"; $error_code = 999; } } // if error flag set if ($error_msg) { // return an XML-RPC fault as an xmlrpcresp object return new xmlrpcresp(0, $error_code, $error_msg); } else { // process data depending on requested output format if ($format == "raw") { // return raw data as associative // array ("format" => $format, "data" = array($t1, $t2, $t3)) // note that the xmlrpcresp object is constructed // from a collection of xmlrpcval objects $data = array(new xmlrpcval($t1, "int"), new xmlrpcval($t2, "int"), new xmlrpcval($t3, "int")); $result = new xmlrpcval(array("format" => new xmlrpcval($format, "string"), "data" => new xmlrpcval($data, "array")), "struct"); return new xmlrpcresp($result); } else if ($format == "avg") { // total and average readings $total = $t1 + $t2 + $t3; // do this to avoid division by zero errors if ($total != 0) { $avg = $total/3; } else { $avg = 0; } // return average as associative array ("format" => $format, "data" = $avg) $result = new xmlrpcval(array("format" => new xmlrpcval($format, "string"), "data" => new xmlrpcval($avg, "int")), "struct"); return new xmlrpcresp($result); } } } ?> And Listing 8.14 demonstrates the code for the client. Listing 8.14 A Simple XML-RPC Client<html> <head> <basefont face="Arial"> </head> <body> <?php // form not yet submitted // display form if(!$_POST['submit']) { ?> <form action="<? echo $_SERVER['PHP_SELF']; ?>" method="POST"> <b>City code:</b> <br> <input type="text" name="city" size="4" maxlength="3"> <p> <b>Data format:</b> <br> <input type="Radio" name="format" value="avg" checked>Average only <br> <input type="radio" name="format" value="raw">Raw data <p> <input type="submit" name="submit" value="Go!"> </form> <?php } else { // include class definitions include("xmlrpc.inc"); // construct array of parameters // this is an associative array of the form ("city" => $city, "format" => $format) $params = new xmlrpcval(array("city" => new xmlrpcval($_POST['city'], "string"), "format" => new xmlrpcval($_POST['format'], "string")), "struct"); // create an XML-RPC request object $msg = new xmlrpcmsg("getWeatherData", array($params)); // uncomment next line to see serialized request // echo $msg->serialize(); // create an XML-RPC client // the object constructor requires the XML-RPC server location as parameters $client = new xmlrpc_client("/rpc/server.php", "weather.domain.com", 80); // uncomment next line to see client debug messages // $client->setDebug(1); // send RPC request, read response // the send() method is a transparent method to send data to the server // it returns the server's response as an xmlrpcresp object $response = $client->send($msg); // create xmlrpcval object to hold value $result = $response->value(); // assuming no fault took place... if (!$response->faultCode()) { // this object is an associative array // of form("format" => $format, "data" = arrayscalar) // get the format $member = $result->structmem("format"); $format = $member->scalarval(); // check data format requested // if raw readings requested if ($format == "raw") { echo "Last three temperature readings for city $city (8-hour intervals) are:<br><ul>"; $member = $result->structmem("data"); // iterate through data array and display values for($x=0; $x<$member->arraysize(); $x++) { $obj = $member->arraymem($x); echo "<li>" . $obj->scalarval(); } echo "</ul>"; } // if average reading requested else if ($format == "avg") { // display single value $member = $result->structmem("data"); echo "Average temperature reading for city $city is " . $member->scalarval() . " (based on three readings)"; } } // if fault generated // display fault information else { // the faultString() and faultCode() methods return // the error string and error code respectively echo "The following error occurred: <br>"; echo $response->faultString() . " (error code " . $response->faultCode() . ")"; } } ?> </body> </html> As demonstrated previously, both client and server objects come with specific methods to ease the task of transmitting an RPC request and decoding the response. A simple send() method handles most of the work at the client end of the connection, whereas ancillary methods such as scalarval() , arraymem(), and structmem() (designed specifically to manipulate composite XML-RPC data types such as <struct> s and <array> s) make it possible to easily convert these data types to native PHP structures. An interesting item here are the addition of the faultString() and faultCode() methods, which allow developers to check for the presence of an XML-RPC fault in the server-generated response and take appropriate measures to handle this fault. This XML-RPC implementation is one of the more robust and full-featured ones available online. In addition to basic RPC functions, it also includes a very capable introspection API, support for HTTPS transactions, and a number of utility functions for converting between PHP and XML-RPC data types. Finally, because it's been under development for awhile, it's also fairly well-documented; if you plan to use it in one of your projects, you might want to visit this book's companion web site, which offers links to articles and reference material on the topic to help you get started. |
I l @ ve RuBoard |