The build on Chris's application is going well and we have finished implementing the core tiered architecture!
The first step was to construct a simple object registry so that the individual components can be shared via a composite pattern. In a nutshell, it is a basic text book Singleton with a twist. The code looks like this:
Singleton object registry class
001
002
003
004
005
006
007
008
009
010
011
012
|
<?php
class Singleton{
function &getInstance ($class, $name = ''){
static $registry = array();
$_instanceName = notNull($name) ? $name : $class;
if ( !isset($registry[$class][$_instanceName]) || !is_object($registry[$class][$_instanceName]) ) {
$registry[$class][$_instanceName] =& new $class;
}
return $registry[$class][$_instanceName];
}
}
?> |
The code above is the backbone of the application since it is completely object oriented. The benefit is that the code (classes or objects) are reusable and are able to be shared among all yet only instantiated once. This adds tremendous flexibility while keeping system resource utilization to a minimum as the registry contains only references to the objects and NOT copies of the objects.
The bottom line is that the category pages are rendering in less than .03 seconds with .005 total MySQL time (of course, no caching yet). Almost forgot...only 5 queries! The performance is blistering fast and infinitely scalable * to the limits of MySQL*. The reason for the outstanding performance is that the application was coded from the bottom up with an eye on performance. Since we are sharing objects we eliminated a lot of redundant database queries and processing overhead with multiple instantiation.
In addition, we have implemented an Observer / Listener pattern. This will form the backbone of the first plug-and-play eCommerce shopping cart! Admittingly, our beta code is far from perfect but has tremendous potential. The idea is the place strategic hooks in the business logic (and maybe objects) which will enable notification to event Listeners. Thus far we have implemented 2 core systems that use the Observer pattern: error logging and also script/performance timers. Here is the base class:
Observer pattern class
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
|
<?php
class Observer{
var $_observers;
var $_message;
function Observer(){
$this->_observers = array();
$this->_message = NULL;
} # end constructor
function attach(&$observer){
$this->_observers[] =& $observer;
} # end function
function detach(&$observer){
foreach ( array_keys($this->_observers) as $key ){
if ($this->_observers[$key] === $observer){
unset($this->_observers[$key]);
return;
} # end if
} # end foreach
} # end function
function notify(){
foreach ( array_keys($this->_observers) as $key ){
$observer =& $this->_observers[$key];
$observer->update($this);
} # end foreach
} # end function
function getState(){
return $this->_message;
} # end function
function setState($info){
$this->_message = $info;
$this->notify();
} # end function
} # end class
?> |
...example instantiation of Observer object and attachment of event hander
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
|
<?php
/////////
// This code is in application_top.php
/////////
/*
* Initialize the error handler (observer) class as a Singleton
* and attach the screen output object
*/
$errorHandler =& Singleton::getInstance('Observer', 'ErrorHandler');
$errorHandler->attach(Singleton::getInstance('ScreenOutputErrorLogger'));
/*
* Set the custom error handler function
*/
set_error_handler('observerErrorHandler');
/////////
// This code is a general (callback) function
/////////
function observerErrorHandler($errno, $errstr, $errfile, $errline, $errcontext){
$handler =& Singleton::getInstance('Observer', 'ErrorHandler');
$handler->setState( array('number' => $errno,
'msg' => $errstr,
'file' => $errfile,
'line' => $errline
)
);
}
?> |
ScreenOutputErrorLogger event handler class
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
|
<?php
class ScreenOutputErrorLogger{
var $_errors;
function ScreenOutputErrorLogger(){
$this->_errors = array();
} # end constructor
function update(&$error_handler){
$error = $error_handler->getState();
$this->_errors[] = $error;
} # end function
function errorCount(){
return sizeof($this->_errors);
} # end function
function output($hidden = false){
if ($hidden) echo '<!--' . "\n";
echo '<h1>Errors:</h1>' . "\n";
echo '<pre>' . "\n";
print_r($this->_errors);
echo '</pre>' . "\n";
if ($hidden) echo '//-->' . "\n";
} # end function
} # end class
?> |
The basic operation is that once an Observer is instantiated other objects can register as an event handler. The great benefit is that there can be multiple event handlers for each! So, the ErrorHandler object is instantiated and then directly after that the ScreenOutputErrorLogger is attached. Thus, every non-fatal error that is triggered will send the Listener state to every object that is registered for notification (right now only the screen logger). The flexibility in this system is that other objects can be registered as well. As an example, we will create the email object next which can be registered as an event handler and email the administrator on database errors (instead of killing the script). In fact, we can create as many event handlers as we want. There could easily be a logger event handler that saves the errors to database or text file. The point is that event handlers can be attached and detached as needed without affecting the function of the others.
Another development milestone is the true tiered architecture. We have implemented a classic 4 tier architecture: presentation (template engine), business logic (meat and bones manipulating object interaction), accessor (database access), and persistence (database). We have tested the architecture by placing each tier on a different server that is physically separate from the others. The application performed beautifully and is confirmation the tiered architecture is complete and bug free.
This ground-up build has enabled me to finally implement my custom rolled template engine! There is no PHP in the templates and is completely separate from the code. Thus, inexperienced webmasters can tweak and modify the templates all they want without ever corrupting the core code. In addition, I feel a robust templating engine will afford more designers to quickly create skins. Here is the template class code:
Template engine class
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
|
<?php
class Template{
var $template;
var $variables;
function Template($template = ''){
$this->template = notNull($template) ? file_get_contents($template) : NULL;
} # end class constructor
function Add($var_name, $var_data){
$this->variables[$var_name] = $var_data;
} # end fucntion Add()
function AddMulti($var_data){
if (is_array($var_data)){
foreach ( $var_data as $name => $data ){
$this->variables[$name] = $data;
}
}
} # end fucntion Add()
function ResetVars(){
$this->variables = NULL;
} # end function
function EvaluateTag($data, $tag, $params = NULL){
$_tag = '<' . $tag;
if (notNull($params)){
$_tag .= ' ' . $params;
}
$_tag .= '>' . $data . '</' . $tag . '>' . "\n";
return $_tag;
} # end function
function Remove($vars){
if ( is_array($vars) ){
foreach ( $vars as $index => $varname ){
unset( $this->variables[$varname] );
} # end foreach
} else {
unset($this->variables[$vars]);
}
} # end fucntion Add()
function Evaluate($direct_output = false){
$template = addslashes($this->template);
foreach ( $this->variables as $variable => $data ){
$$variable = $data;
} # end foreach
eval("\$template = \"$template\";");
if ( $direct_output ) echo stripslashes($template);
else return stripslashes($template);
} # end function Evaluate()
} # end class
?> |
As you can see, we are making good progress on the build and will soon move onto finalizing the beta version of the shopping cart! However, there is still much left to do in order to launch this project. I still need to coordinate the acquisition of a dedicated server to host the project Subversion server. In addition, we need to attract a community to help in the beta testing. It is almost time to launch!
...almost there...stay tuned!