Archive for the 'PEAR' Category

Website monitoring pays off

I’ve been monitoring the perfromance and availability of some of my websites for two years now. By measuring things like how long it takes to connect to the web server, how long it takes for the web server to respond, and how long it takes to download all the content, I’ve been able to improve the performance of several websites, including of course, BritBlog.

The website monitoring service that I use also alerts me by SMS and EMail when the web sites die, or if they fail to respond in the expected fashion. This has been particularly handy for BritBlog which runs on a rather temperamental server!

Read the rest of this entry »

XML-RPC autodiscovery

You probably know by know that I’ve been playing with PEAR and XML-RPC a lot lately. Well when reading an excellent book called Advanced PHP Programming I came across a small section on XML-RPC autodiscovery. This seemed like an interesting thing to me (no, really it did), so I decided to have a play.

Now my only problem with this book is that most of the stuff is written for PHP5. This wouldn’t be so bad (PHP 5 looks to have to great improvements), except for the fact that most of the web servers I have to work on all run PHP 4.

So, for the benefit of the human race, I’ve re-written the script so that it will work in PHP4:

require_once('XML/RPC.php');

$url = parse_url('http://www.oreillynet.com/meerkat/xml-rpc/server.php');
//$url = parse_url('http://www.syndic8.com/xmlrpc.php');
//$url = parse_url('http://www.newsisfree.com/RPC');
//$url = parse_url('http://localhost/index.php');

$cli   = new XML_RPC_Client($url['path'], $url['host']);
$msg   = new XML_RPC_Message('system.listMethods');
$resp = $cli->send($msg);

if($resp->faultCode())
{
   echo "** ERROR **n";
   echo 'Fault Code: ' . $result->faultCode() . "n";
   echo 'Fault Reason: ' . $result->faultString() . "n";
   exit;
}

// get list of methods returned
$methods = XML_RPC_decode($resp->value());

foreach($methods as $method)
{
   print "Method $method:n";

   // get the documentation for this method
   $oDoc = $cli->send(
                      new XML_RPC_Message('system.methodHelp',
                      array( new XML_RPC_Value($method) ))
                      );

   $doc = XML_RPC_decode( $oDoc->value() );

   if($doc)
   {
      print "$docn";
   }
   else
   {
      print "No Docstringn";
   }

   // get the method signatures (if there are any)
   $msg = new XML_RPC_Message('system.methodSignature',
                              array(new XML_RPC_Value($method)));
   $oResp = $cli->send($msg);
   $resp = $oResp->value();

   if($resp->kindOf() == 'array')
   {
      $sigs = XML_RPC_decode($resp);
      for($i = 0; $i < count($sigs); $i++)
      {
         $return = array_shift($sigs[$i]);
         $params = implode(", ", $sigs[$i]);
         print "Signature #$i: $return $method($params)n";
      }
   }
   else
   {
      print "No Signaturen";
   }
   print "n";
}

Save this file as something like rpc-test.php and you can run this from the command line like this:

php -f rpc-test.php

Depending on the server you point it at, you will get output like the following (in this case from Meerkat, O’Reilly Network’s Open Wire Service):

Method meerkat.getChannels:
Returns an array of structs of available RSS channels each with its associated channel Id.
Signature #0: array meerkat.getChannels()

Method meerkat.getCategories:
Returns an array of structs of available Meerkat categories each with its associated category Id.
Signature #0: array meerkat.getCategories()

Method meerkat.getCategoriesBySubstring:
Returns an array of structs of available Meerkat categories each with its associated category Id given a substring to match (case-insensitively).
Signature #0: array meerkat.getCategoriesBySubstring(string)

Method meerkat.getChannelsByCategory:
Returns an array of structs of RSS channels in a particular category (specified by integer category id) each with its associated channel Id.
Signature #0: array meerkat.getChannelsByCategory(int)

Method meerkat.getChannelsBySubstring:
Returns an array of structs of RSS channels in a particular category each with its associated channel Id givena substring to match (case-insensitively).
Signature #0: array meerkat.getChannelsBySubstring(string)

Method meerkat.getItems:
Returns an array of structs of RSS items given a recipe struct.

Search Criteria:
  channel (int) - a channel's numeric id
  category (int) - a category's numeric id
  item (int) - a particular item's numeric id
  search (string) - /a MySQL regular expression/
	time_period (string) - travel back in time; format: #(MINUTE|HOUR|DAY)
  profile (int) - a particular profile's numeric id
  mob (int) - a particular mob's numeric id
  url (string) - a particular url

Display Recipes:
  ids (int) - Return story ids (0=off, 1=on)
  descriptions (int) - Return item descriptions (0=off, 1=full, >1=length)
  categories (int) - Return categories with which items are associated (0=off, 1=on)
  channels (int) - Return channels in which items appeared (0=off, 1=on)
  dates (int) - Return the dates items were picked up by Meerkat (0=off, 1=on)
  num_items (int) - The number of items to be returned (0 < n <=50)

Example:
  $ f = new xmlrpcmsg("meerkat.getItems",
    array(
      new xmlrpcval(
        array(
          "channel" => new xmlrpcval(724, "int"),
          "search" => new xmlrpcval("/xml|[Jj]ava/", "string"),
          "time_period" => new xmlrpcval("3DAY", "string"),
          "ids" => new xmlrpcval(0, "int"),
          "descriptions" => new xmlrpcval(200, "int"),
          "categories" => new xmlrpcval(0, "int"),
          "channels" => new xmlrpcval(0, "int"),
          "dates" => new xmlrpcval(0, "int"),
          "num_items" => new xmlrpcval(5, "int"),
      	),
        "struct"
      )
    )
  );

Signature #0: array meerkat.getItems(struct)

Method system.listMethods:
This method lists all the methods that the XML-RPC server knows how to dispatch
Signature #0: array system.listMethods(string)
Signature #1: array system.listMethods()

Method system.methodHelp:
Returns help text if defined for the method passed, otherwise returns an empty string
Signature #0: string system.methodHelp(string)

Method system.methodSignature:
Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)
Signature #0: array system.methodSignature(string)

Caveat

You may run into some problems when running this script: not all servers make the system methods available to them (like system.listMethods, system.methodHelp, and system.methodSignature

Anyway, I hope someone finds this useful/interesting…

Word of the Day - MediaWiki Extension

I’ve written my first MediaWiki extension - it’s for displaying the Dictionary.com Word of the Day on your Wiki.

I may have been a bit naughty because this extension prefers it if you use PEAR. You don’t have to though - you can just download the two pear packages (XML_RSS and Cache_Lite - two packages that are great!) and dump them somewhere on your include path.

I’ve written this as I’ve just started getting into this Wiki stuff. I’ve set one up at work, and so far I seem to be getting a lot of enthusiasm from my colleagues about it. Time will tell what happens!

Anyway, why not take a look at my WikiMedia Word of the Day extension! Any feedback will be appreciated :)

PEAR Cache_Lite version 1.5.1 released

PEAR Cache_Lite 1.5.1 has been released, so you should go and get it now. I’ve said it before, and I’ll probably say it again, but I do like PEAR - especially when the packages are well maintained like this one is!

New PEAR XML_RPC Release

As you may have guessed, I’m currently playing around with the PEAR XML_RPC package (cf. XML-RPC weblogUpdates.ping). If you too are using it, you may like to know that there is a new release out today - version 1.3.2.

Using XML-RPC and Base64 encoded binary data

I’ve been looking at ways to improve the performance of the new BritBlog server, and wherever possible I try to use components from the PHP Extension and Application Repository (PEAR).

For example, I’ve found PEAR Cache_Lite to be an excellent tool for caching dynamic content that doesn’t change that frequently, and I use things like XML_Tree and XML_RSS when handling XML and RSS.

What about XML RPC?

So in my quest to remove load from the server I’ve been looking into the possibility of generating dynamic images (maps and graphs, for example) off-site, and then transferring them to the server over XML RPC, using the PEAR XML_RPC package.

So is it possible? The short answer is ‘Yes’! The trick is to base64 encode the image data. You can either do this with the PHP base64_encode() method, and then specify the XML-RPC data type of string, or you can specify the data type of base64 and let XML-RPC take care of the encoding itself.

A note on base64 encoding from the PHP manual:

base64_encode() returns data encoded with base64. This encoding is designed to make binary data survive transport through transport layers that are not 8-bit clean, such as mail bodies.

Base64-encoded data takes about 33% more space than the original data.

As Base64 is a supported data type for XML-RPC then this should be used by preference. Setting the data type to Base64 also makes the encoding happen transparently, which means there is one less thing for you to worry about.

So here is are two little scripts to demonstrate this, using the PEAR XML_RPC package. (They are based heavily on the example scripts in the documentation.) Create two files, server.php and client.php and stick the in a folder called xml-rpc in the root directory of a web server. Now copy the code below into the two files.

Now if you open http://localhost/xml-rpc/client.php (assuming you are running the web server locally), the client will make the request to the web service getImage and then print the image that is returned to your screen.

server.php:

<?php
require_once 'XML/RPC/Server.php';

/*
 * Method to return an image. Perhaps this
 * generates an image on-the-fly.
 */
function getImage()
{
   // We're just going to open an existing image
   if($img = @file_get_contents('graph.png'))
   {
      $val = new XML_RPC_Value($img, 'base64');
      return new XML_RPC_Response($val);
   }

   // Ooops, couldn't open the file so return an error message.
   return new XML_RPC_Response(0, $XML_RPC_erruser+1, // user error 1
			      'There was an error opening the file.');
}

// map web services to methods
$server = new XML_RPC_Server(
   array(
      'getPhoto' =>
         array('function' => 'getImage'),
   )
);
?>

client.php:

<?php
require_once 'XML/RPC.php';

$msg = new XML_RPC_Message('getPhoto'); // getPhoto is the service we want to call

$cli = new XML_RPC_Client('/xml-rpc/server.php', 'localhost');
$cli->setDebug(0);

// send the request message
$resp = $cli->send($msg);

if (!$resp)
{
   echo 'Communication error: ' . $cli->errstr;
   exit;
}

if (!$resp->faultCode())
{
   // All OK, so print the image
   $val = $resp->value();
   header('Content-type: image/png');
   echo $val->scalarval();
}
else
{
   /*
    * Display problems that have been gracefully cought and
    * reported by the xmlrpc.php script.
    */
   echo 'Fault Code: ' . $resp->faultCode() . "n";
   echo 'Fault Reason: ' . $resp->faultString() . "n";
}
?>

So there you have it. As is common with code using PEAR packages, it’s nice and simple!

I’m not sure if I’ll start using this kind of stuff for BritBlog, but it shows an interesting application for XML-RPC.

Using PEAR Cache_Lite

Cache_Lite is a PHP PEAR package designed for caching stuff. As the project description puts is:

This package is a little cache system optimized for file containers. It is fast and safe (because it uses file locking and/or anti-corruption tests).

The documentation for this project is actually quite good (I’m not impressed by the level of documentation for the majority of PEAR packages), so I would suggest you pop over there and take a look at it.

Anyway, I’ve just written a little bit of code to get the dictionary.com Word of the Day and cache it for two hours, so I thought I’d share it here. The script also uses the XML_RSS package, which is a handy little tool when you’re dealing with RSS.

<?php
/*
** This script demonstrates some simple useage of two PHP PEAR packages
** (Cache_Lite and XML_RSS).
**
** When run it displays the dictionary.com Word of the Day. First of
** all, it looks to see if we have a local cached copy of the Word of
** the Day. If not, it gets the Word of the Day RSS feed from
** dictionary.com, pulls out the current Word of the Day, and then
** caches it locally ready for use the next time.
*/

// include the PEAR packages
require_once('XML/RSS.php');
require_once('Cache/Lite.php');

// name a cache key
$cache_id = 'wotd';

// the data (an array) that we want to cache
$data = array();

// Set a few Cache_Lite options
$options = array(
    'cacheDir' => '/tmp/',      // must include trailing slash…
    'lifeTime' => 7200,              // cache life in seconds (2 hours)
    'pearErrorMode' => CACHE_LITE_ERROR_DIE  // helps us when debugging
                                    // but not good for production site
);

// Create a Cache_Lite object
$cache = new Cache_Lite($options);

// Test if there is a valide cache item for this data
if( $data = $cache->get($cache_id) )
{
    // Cache hit
    $data = unserialize($data);
}
else
{
   // Cache miss
   $rss =& new XML_RSS('http://dictionary.reference.com/wordoftheday/wotd.rss');
   $rss->parse();
   foreach ($rss->getItems() as $item)
   {
      $data['link'] = $item['link'];
      list($data['title'], $tmp) = explode(':', $item['title']);
      // we only need to get the first item, so jump out here
      break;
   }
   $cache->save(serialize($data));
}

// print out the results of our work!
echo 'Word of the day: <a href="' . $data['link'] . '">' . $data['title'] . '</a>';

?>

So there you have it!

It’s pretty simple really, but the package looks to be quite powerful. I’m sure the other caching package, PEAR Cache is good too, but the documentation here is rather poor :-(

PEAR Cache_Lite

I’ve just been looking over one of the pear caching packages: Cache_Lite. It’s too late to start playing with it properly, but it looks like a very cool package - especially with the wrapper around PHP’s output buffering features. This should make it really nice and easy to integrate with BritBlog, so I’m hoping to see some good performance improvements once I start caching the directory pages with this…

There is another PEAR cache package (Cache), but the documentation isn’t as clear as Cache_Lite’s. It really annoys me that some people can spend such a lot of time working on great software (and this other package looks really well featured), only to write rubbish documentation (if they write any at all).