_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 .= ""; $html .= ""; $i = 0; foreach($observer->getEvents() as $event) { $html .= "\n\t"; $html .= "\n\t\t"; $html .= "\n\t\t"; $html .= "\n\t\t"; $html .= "\n\t"; $i++; } $html .= "\n
QueryQuery TimeAffected Rows
\n\t\t\t" . $event->getQuery() . "\n\t\t\n\t\t\t" . $event->getTime() . "\n\t\t\n\t\t\t" . $event->getRows() . "\n\t\t
"; echo $html; ?>