An Alternative PHPXML-RPC Implementation: XML-RPC for PHP

I l @ ve RuBoard

An Alternative PHP/XML-RPC Implementation: XML-RPC for PHP

Before 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.

Pre-Flight Check

All the examples in this section use version 1.02 of XML-RPC for PHP.

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", graphics/ccc.gif "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 graphics/ccc.gif 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 graphics/ccc.gif 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 graphics/ccc.gif names  $server = new xmlrpc_server(array("getWeatherData" => array("function" => graphics/ccc.gif "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 = graphics/ccc.gif "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. " graphics/ccc.gif .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 graphics/ccc.gif xmlrpcval($t3, "int"));                    $result = new xmlrpcval(array("format" => new xmlrpcval($format, graphics/ccc.gif "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" = graphics/ccc.gif $avg)                    $result = new xmlrpcval(array("format" => new xmlrpcval($format, graphics/ccc.gif "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"), graphics/ccc.gif "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) graphics/ccc.gif 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 " . graphics/ccc.gif $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() . graphics/ccc.gif ")";        }  }  ?>  </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


XML and PHP
XML and PHP
ISBN: 0735712271
EAN: 2147483647
Year: 2002
Pages: 84

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net