<?php

//--------------------------------------------
// A dispatcher. This class deals with managing
// observer classes and notifying these observers
// about events.
//--------------------------------------------

class FADispatcher 
{
    
//--------------------------------------------
    // An array of the observers that this dispatcher
    // will notify about incoming events.
    //--------------------------------------------
    
    
var $_observers = array();
    
    
//--------------------------------------------
    // Add an observer to the above array so that when
    // the dispatcher is told to notifyAll (notify all
    // observers) then we will have an/some observers to
    // notify!
    //--------------------------------------------
    
    
function addObserver(&$observer
    {
        
//--------------------------------------------
        // Make sure that it's actually an observer being
        // passed in.
        //--------------------------------------------
        
        
assert(is_a($observer'FAObserver'));
        
        
//--------------------------------------------
        // Add an observer to the array of observers.
        //--------------------------------------------
        
        
$this->_observers[] = &$observer;
    }
    
    
//--------------------------------------------
    // Notify all of the observers in the _observers
    // array about $event.
    //--------------------------------------------
    
    
function notifyAll(&$event
    {
        
//--------------------------------------------
        // Make sure the even being passed is observable.
        //--------------------------------------------
        
        
assert(is_a($event'FAObservableEvent'));
        
        
$ret FALSE;

        for(
$i 0$i count($this->_observers); $i++)
        {
            
//--------------------------------------------
            // Get one of the observers.
            //--------------------------------------------
            
            
$observer = &$this->_observers[$i];
            
            
//--------------------------------------------
            // If an observer has successfully been notified,
            // stop notifying observers.
            //--------------------------------------------
            
            
if($ret = (bool)$observer->notify($event)) 
            {
                break;
            }
        }
        
        return 
$ret;
    }
}

//--------------------------------------------
// The base observer class. This class cannot
// be used directly.
//--------------------------------------------

class FAObserver 
{
    
//--------------------------------------------
    // Notify this observer about an incoming event.
    //--------------------------------------------
    
    
function notify(&$event
    {
        
assert(FALSE);
    }
}

//--------------------------------------------
// The observable event class... Not much to say
// here.
//--------------------------------------------

class FAObservableEvent 
{
}

//--------------------------------------------
// An observable database query event. This class holds
// information about a specific database query.
//--------------------------------------------

class DatabaseQueryEvent extends FAObservableEvent 
{
    var 
$_sql;
    var 
$_rows;
    var 
$_time;
    
    
//--------------------------------------------
    // Constructor. The variables passed in are:
    // $sql - the SQL query
    // $rows - the number of rows that the sql query affected
    // $time - the number of microseconds that the query took
    //         to execute.
    //--------------------------------------------
    
    
function DatabaseQueryEvent($sql$rows 0$time 0
    {
        
$this->_sql $sql;
        
$this->_rows $rows;
        
$this->_time $time;
    }
    
    
//--------------------------------------------
    // Get the SQL of the query used.
    //--------------------------------------------
    
    
function getQuery() 
    {
        return 
$this->_sql;
    }
    
    
//--------------------------------------------
    // Get the number of rows that the query affected.
    //--------------------------------------------
    
    
function getRows() 
    {
        return 
$this->_rows;
    }
    
    
//--------------------------------------------
    // Get the number of microseconds that the query
    // took to execute.
    //--------------------------------------------
    
    
function getTime()
    {
        return 
$this->_time;
    }
}

//--------------------------------------------
// The database observer class. This class collects
// database query events.
//--------------------------------------------

class DatabaseObserver extends FAObserver 
{
    
//--------------------------------------------
    // An array of database query events.
    //--------------------------------------------
    
    
var $_events = array();
    
    
//--------------------------------------------
    // Notify this observer about an event. In this case
    // it will be a database query event. Push that event
    // onto the array of events.
    //--------------------------------------------
    
    
function notify(&$event
    {
        
$this->_events[] = $event;
    }
    
    
//--------------------------------------------
    // Return the array of all of the database query
    // events.
    //--------------------------------------------
    
    
function getEvents() 
    {
        return 
$this->_events;
    }
}

//--------------------------------------------
// Our base class for the database abstraction layer.
// This class deals with setting up and managing the
// dispatcher that we are using.
//--------------------------------------------

class FADatabaseConnection
{
    
//--------------------------------------------
    // The dispatcher that the database abstraction layer
    // will use to notify observers about events.
    //--------------------------------------------
    
    
var $_dispatcher;
    
    
//--------------------------------------------
    // Constructor, instanciate a new dispatcher to deal
    // with notifying any observers added to it about events.
    //--------------------------------------------
    
    
function FADatabaseConnection()
    {
        
$this->_dispatcher = &new FADispatcher();
    }
    
    
//--------------------------------------------
    // Add observers to this dispatcher so that when this
    // dispatcher is notified about events, it can tell
    // the observers about them.
    //--------------------------------------------
    
    
function addObserver(&$observer)
    {
        
$this->_dispatcher->addObserver($observer);
    }
    
    
//--------------------------------------------
    // Notify any observer in the dispatcher about
    // an event.
    //--------------------------------------------
    
    
function notifyAll(&$event)
    {
        
$this->_dispatcher->notifyAll($event);
    }
    
    
//--------------------------------------------
    // The execute query method, this method will execute
    // a query on a database.
    //--------------------------------------------
    
    
function executeQuery($sql '')
    {
        
assert(FALSE);
    }
}

//--------------------------------------------
// This would normally be an abstraction layer for
// something like MySQL or PostgreSQL... For this example,
// we are dealing with the ever ambiguous SomeSQL!
//--------------------------------------------

class SomeSQL extends FADatabaseConnection
{
    
//--------------------------------------------
    // Execute a database query
    //--------------------------------------------
    
    
function executeQuery($sql)
    {
        
//--------------------------------------------
        // Now, these two variables would normally take
        // their values from something like mysql_affected_rows
        // and by putting two microtime()'s around the execution
        // of the query and finding the difference. But, we won't
        // do that in this example.. mwahahaha.
        //--------------------------------------------
        
        
$execution_time microtime();
        
$affected_rows 0;
        
        
//--------------------------------------------
        // Normally one would call the query method for a database here.
        //--------------------------------------------
        
        //--------------------------------------------
        // Aha! Instanciate a new database query event
        // (an observable event) and pass it to the notifyAll
        // function. The notifyAll function calls FADispatcher::notifyAll
        // which in turn calls FAObserver::notify! What a nice
        // chain of events.
        //--------------------------------------------
        
        
$this->notifyAll(new DatabaseQueryEvent($sql$affected_rows$execution_time));
    }
}

//--------------------------------------------
// Instanciate our observer. By the end of execution,
// this observer will hold and array of information
// for all of the database queries.
//--------------------------------------------

$observer = &new DatabaseObserver;

//--------------------------------------------
// Instanciate our SomeSQL database abstraction layer
// and add the above observer to the dispatcher that
// was implicitly instanciated when SomeSQL was.
//--------------------------------------------

$dba = &new SomeSQL;
$dba->addObserver($observer);

//--------------------------------------------
// Execute a dummy query just for the sake of this
// example.
//--------------------------------------------

$dba->executeQuery("SELECT moo FROM cow WHERE alive=1");

//--------------------------------------------
// Debug time! Let's loop over all of the query
// events stored in the observer and make a table
// with some debug information.
//--------------------------------------------

$html "";
$html .= "<table border=\"1\">";
$html .= "<tr><th>Query</th><th>Query Time</th><th>Affected Rows</th></tr>";

$i 0;
foreach(
$observer->getEvents() as $event) {
    
$html .= "\n\t<tr>";
    
$html .= "\n\t\t<td>\n\t\t\t" $event->getQuery() . "\n\t\t</td>";
    
$html .= "\n\t\t<td>\n\t\t\t" $event->getTime() . "\n\t\t</td>";
    
$html .= "\n\t\t<td>\n\t\t\t" $event->getRows() . "\n\t\t</td>";
    
$html .= "\n\t</tr>";

    
$i++;
}
$html .= "\n</table>";

echo 
$html;
?>