I have introduced FirePHP Companion and you say it looks like a cool tool, but what can you actually do with it that is going to help you? This is the first post that looks at practical applications.
To start with I thought I would rework the example I included in my php|architect article. You will see how much easier it now is to accomplish the same, and with much less code, using FirePHP Companion instead of the existing FirePHP extension.
The Task
Let’s say you want to display an RSS feed on your site. You know that you should cache the feed on your server and only fetch updates from the origin server on a periodic basis. You know this because things get real slow if you don’t do that and you may get blocked from accessing the feed altogether if you call it with every request.
Knowing you need to build some caching logic it would be nice to have a tool that can help you see what is going on when you make a request to your script. You may want to know where your cache file is on disk, how stale it is, if the feed was re-fetched and the cache was updated and so on. This is exactly where FirePHP Companion comes in.
The Cumbersome Way
The technique I described in the php|architect article works, but admittedly is not the ideal solution as it is not necessarily that simple and requires a lot of framing code. To see what I mean you can take a look at the demo here and download the code here. Make sure you have the FirePHP extension installed to run the demo.
The FirePHP Companion Way
Now let’s look at how the same kind of problem is solved with FirePHP Companion. I am assuming you have a virtual host setup and working with FirePHP Companion. We only need two files and here they are:
index.php
<?php /*i*/ // Denotes instructions used for insight that may be stripped out // Bootstrap File /* NOTE: You must have the FirePHP library on your include path */ /*i*/ define('INSIGHT_IPS', '*'); /*i*/ define('INSIGHT_AUTHKEYS', '<your-AUTHORIZATION_KEY>'); /*i*/ define('INSIGHT_PATHS', __DIR__); /*i*/ define('INSIGHT_SERVER_PATH', './index.php'); /*i*/ require_once('FirePHP/Init.php'); /*i*/ FirePHP::plugin('firephp')->trapProblems(); /*i*/ FirePHP::plugin('firephp')->recordEnvironment( /*i*/ FirePHP::to('request')->console('Environment')->on('Show Environment') /*i*/ ); /*i*/ FirePHP::to('request')->console('Feed')->info('Startup'); // Application Code require_once(__DIR__ . '/feed.php'); $feed = new Feed('http://www.phpdeveloper.org/feed'); $items = $feed->getItems(); if($feed->didLoad()) { echo '<p><b>Loading feed from: '.$feed->getUrl().'</b></p>'."\n"; } foreach( $items as $item ) { echo '<p><a href="'.$item['link'].'">'.$item['title'].'</a></p>'."\n"; } /*i*/ FirePHP::to('request')->console('Feed')->info('Shutdown');
feed.php
<?php class Feed { /*i*/ private $console; private $url = false; private $ttl = 10; // in seconds private $didLoad = false; public function __construct($url) { $this->url = $url; /*i*/ $this->console = FirePHP::to('request')->console('Feed')->on('Feed Debug'); } public function getUrl() { return $this->url; } public function didLoad() { return $this->didLoad; } public function getItems() { $this->didLoad = false; $file = $this->getCachePath(); /*i*/ $this->console->label('Cache File')->log($file); /*i*/ $this->console->label('Cache File Exists')->log(file_exists($file)); if(file_exists($file)) { $fileTime = filemtime($file); $fileTtl = time()-$fileTime; /*i*/ $this->console->label('Cache Time Remaining')->log($this->ttl-$fileTtl); } else { $fileTime = false; $fileTtl = 0; } if($fileTtl >= $this->ttl /*i*/ || FirePHP::to('request')->console('Feed')->on('Force Reload Cache')->is(true) ) { /*i*/ $this->console->info('Deleting Cache File'); if(file_exists($file)) { unlink($file); } } if(!file_exists($file)) { /*i*/ $group = $this->console->group()->open(); /*i*/ $this->console->log('Load feed and store in cache file'); $this->load(); /*i*/ $group->close(); /*i*/ } else { /*i*/ $this->console->info('Skip load as feed is cached'); } $json = json_decode(file_get_contents($file), true); /*i*/ $this->console->label('Feed data')->log($json); return $json; } private function load() { $this->didLoad = true; $file = $this->getCachePath(); /*i*/ $this->console->label('URL')->log($this->url); $startTime = microtime(true); $content = utf8_encode(file_get_contents($this->url)); /*i*/ $this->console->label('Load Time')->log(round(microtime(true)-$startTime,5)); /*i*/ $this->console->label('Raw Feed Data')->log($content); $xml = simplexml_load_string($content, 'SimpleXMLElement', LIBXML_NOCDATA); if(!$xml) { /*i*/ $this->console->error('Error parsing XML data'); throw new Exception('Error parsing XML data'); } /*i*/ $this->console->label('Parsed Feed XML')->log($xml); $data = array(); foreach( $xml->channel->item as $item ) { $data[] = array( 'title' => (string)$item->title, 'link' => (string)$item->link ); } /*i*/ $this->console->label('Final JSON Data')->log($data); file_put_contents($file, json_encode($data)); /*i*/ if(file_exists($file)) { /*i*/ $this->console->info('Saved cache file'); /*i*/ } else { /*i*/ $this->console->error('Error writing to cache file'); /*i*/ } } private function getCachePath() { return __DIR__ . '/cache.txt'; } }
Instead of providing a list of instructions on how to use FirePHP Companion with the code above I have put together a short screencast which I am hoping is much more convenient.
Summary
FirePHP Companion can help in understanding what is going on with your application. All you need to do is add logging calls in strategic places and hit refresh! Feel free to leave the code as is when you publish to production servers as the internal data will only be available to authorized clients. Things are optimized to keep overhead to a minimum for production requests on most applications.
There are still some features missing to make FirePHP Companion part of your daily workflow and that is where your feedback comes in. I encourage you to post your experience with FirePHP Companion on the mailing list and let me know what is missing for you.