I have broken this example into two parts. I will show how to create a server component that is designed to run beneath a Web server and how to create a client component. The client piece is generic and written for the command line, but you can expand and embed it in a local Web page. The service allows the client to request a record based on an ID from a database. The request is packaged in a WDDX packet. The server receives the packet unserialized, and the requested record returned is packaged in a WDDX packet to the client.
■ Caution These scripts do not implement any form of security, and data is sent in plain text. When creat- ing a Web service, authentication and secure transmissions are often important to implement. You could do this using combinations of secure sockets, authentication mechanisms, and encryption. For instance, one possible combination is SSL, XML signatures, and XML encryption.
WDDX Web Service Server
The database used in this example is SQLite, because by default it is included in almost every PHP 5+ build. It may be necessary to repath or modify permissions for the database, wddxdb, to be created. The server accepts only those POST requests sending a WDDX packet contain- ing a structure with a varelement having a nameattribute set to recid. This element contains the ID of the record to be retrieved from the database.
The server then retrieves the idand namefields from the database based on the recidand serializes the results into a WDDX packet, which is then sent to the client. This example uses the sqllite_fetch_all()function so you can expand the example and request a range of record IDs. The function returns a multidimensional array, which is easily serialized using thewddx_serialize_value()function.
■ Note To try this example, the server code should reside in a directory accessible by the Web server and be named wddxserver.php. Using a different name for this script will require the name to be changed in the client portion as well.
Here’s the code:
<?php
/* If the database does not exist, then create it and populate it with some data */
if (! file_exists('wddxdb')) {
if ($dbhandle = sqlite_open('wddxdb', 0666)) {
sqlite_query($dbhandle, 'CREATE TABLE wddx (id int, name varchar(15))');
for ($x=1; $x< 11; $x++) { sqlite_query($dbhandle,
"INSERT INTO wddx VALUES (".$x.", 'Data Num: ".$x."')");
}
sqlite_close($dbhandle);
} }
/* Function to retrieve data from database and return the results in a serialized WDDX packet. Upon failure return a NULL value in the packet */
function getDBData($recid) {
if (is_numeric($recid) && $dbhandle = sqlite_open('wddxdb')) {
$query = sqlite_query($dbhandle,
'SELECT id, name FROM wddx where id='.$recid);
$result = sqlite_fetch_all($query, SQLITE_ASSOC);
return wddx_serialize_value($result);
} else {
return wddx_serialize_value(NULL);
} }
/* Requests are only accepted from a POST with the data set in the packet variable. */
if (isset($_POST['packet'])) {
$wddx_packet = $_POST['packet'];
/* retrieve data based on the requested recid, and return resulting packet */
if ($wddx_packet && $arData = wddx_deserialize($wddx_packet)) { if (is_array($arData) && array_key_exists('recid', $arData)) {
print getDBData((int)$arData['recid']);
exit;
} } }
/* On bad requests send a NULL value in the packet */
print wddx_serialize_value(NULL);
?>
WDDX Web Service Client
The client portion is simple. The majority of the code is handling the sockets to the server for posting and retrieving data, as well as the displayed output of the results. The sample code was designed to be run at a command though can be embedded within HTML to oper- ate under a Web server. The first three variables must be set correctly to point to the server hosting the wddxserver.phpfile, which is the server piece of this example, and to the port for the Web server accepting HTTP requests. The connection will be made using TCP, set by the
$remote_protocolvariable.
Here’s the code:
<?php
/* Address of remote server - Set these to the server and port where the remote server script is located. */
$remote_protocol = 'tcp';
$remote_server = 'localhost';
$remote_server_port = 80;
/* The serialized packet. In this case, being an example, it is hard-coded to request the record having an id of 5. */
$packet = wddx_serialize_value(array('recid'=>5));
/* Make POST request using sockets */
$remote_connect = $remote_protocol.'://'.$remote_server;
$sock = fsockopen($remote_connect, $remote_server_port, $errno, $errstr, 30);
if (!$sock) die("$errstr ($errno)\n");
/* Use var name packet for the POST */
$data = 'packet='.urlencode($packet);
fwrite($sock, "POST /wddxserver.php HTTP/1.0\r\n");
fwrite($sock, "Host: $remote_server\r\n");
fwrite($sock, "Content-type: application/x-www-form-urlencoded\r\n");
fwrite($sock, "Content-length: " . strlen($data) . "\r\n");
fwrite($sock, "Accept: */*\r\n");
fwrite($sock, "\r\n");
fwrite($sock, "$data\r\n");
fwrite($sock, "\r\n");
$headers = "";
while ($str = trim(fgets($sock, 4096)))
$headers .= "$str\n";
$packet = "";
while (!feof($sock))
$packet .= fgets($sock, 4096);
fclose($sock);
/* END POST Request */
/* Unserialize packet data, and output resulting data */
$arData = wddx_deserialize($packet);
if (is_array($arData)) { if (count($arData) > 0) {
foreach ($arData AS $rownum=>$arRow) {
foreach ($arRow AS $fieldname=>$fieldvalue) { print $fieldname.": ".$fieldvalue."\n";
}
print "\n";
} } else {
print "No Records Returned";
} } else {
/* Some type of error happened */
var_dump($arData);
}
?>
Because this is merely an example, the record to be retrieved has been hard-coded to 5.
This effectively is requesting the record from the database with an ID of 5. It is fairly trivial to modify this example to make the record number dynamic as well as to allow ranges of records to be requested at once. Rather than serializing a variable named recid, it is just as easy to serialize an associative array mapping recidto the record number being requested.
Once the packet has been created, the client opens a socket connection to the server and posts the data, using the variable packetto identify the WDDX packet being sent. Once the server has processed the request within the packet, it returns the results in another WDDX packet. If it unserializes into an array, it is safe to assume, based on the server script, that the data was queried and returned. This does not mean that any rows were returned, just that no errors were encountered. An unserialized value of NULLindicates that some problem occurred.
I did not break errors out in the example, so this could include a problem accessing the data- base and an improper request to the Web server.
The data returned, if an array, is a multidimensional array. This is the format returned by the sqlite_fetch_all()function. Only simple output processing takes place here, printing each field name and value on a single line. Each row, if the script were expanded to allow mul- tiple record requests, would be separated by two lines. Modifying this example to perform more complex operations and different output is an exercise I will leave to you.