Go Back   SitePoint Forums > Forum Index > Program Your Site > PHP > PHP Application Design
Newsletter FAQ Members List Calendar Mark Forums Read

New to SitePoint Forums? Register here for free!

SitePoint Sponsor
 
Reply
 
Thread Tools Display Modes
Old Mar 31, 2005, 07:53   #1
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Design Patterns Catalogue

Hello, I got thinking today that with all the discussion about patterns, that what we need is a catelog of patterns that us PHP developers use day to day, so I've started this thread.

The idea is that members who use a pattern, any will do I suppose so long as the posted example works, has the revalant underlying script, etc and is PHP, will be as kind to post it to this thread?

Then, whenever someone reads about a particular pattern, or even post, they can start a thread about it, to discuss their issue(s) further in regards to that pattern. The idea is that this thread will be a catelog of pattern examples, with no discussion, so to avoid bloating this thread.

SweatJe is about to publish a new book later this year (summer) on PHP patterns, and I get the idea that this thread could be a supplement to that book, so what someone can't find in SweatJe's book, they'll maybe find it here, in this thread...

Anyways, I'll kick things off with two examples

Decorator
---------

Purpose of this pattern is to add extra functionality to the decorated class at run time. So you are not altering the class(es) that are to be decorated, thus you are extending the life of the said class(es).

PHP Code:

abstract class AbstractRating {

        
protected $decoratable;
        
        
public function __construct( $decoratable ) {
            
$this -> decoratable = $decoratable;
        }
        
        
abstract public function getRating();
    }

    class
PoorRating extends AbstractRating {
        
protected $rating = 1;
        
        
public function __construct( $decoratable ) {
            
parent::__construct( $decoratable );
        }
        
        
public function getRating() {
            return
$this -> decoratable -> getRating() + $this -> rating;
        }
    }
    
    class
AverageRating extends AbstractRating {
        
protected $rating = 2;
        
        
public function __construct( $decoratable ) {
            
parent::__construct( $decoratable );
        }
        
        
public function getRating() {
            return
$this -> decoratable -> getRating() + $this -> rating;
        }
    }
    
    class
GoodRating extends AbstractRating {
        
protected $rating = 3;
        
        
public function __construct( $decoratable ) {
            
parent::__construct( $decoratable );
        }
        
        
public function getRating() {
            return
$this -> decoratable -> getRating() + $this -> rating;
        }
    }
    
    class
Ratings {
        
private $rating = 3;
        
        
public function __construct() {
        }
        
        
public function getRating() {
            return
$this -> rating; // typeof integer
        
}
    }
    
    
$rating = new GoodRating( new AverageRating( new Ratings ) );
    
// we are decorating the Ratings object
    
echo( $rating -> getRating() );
ViewHelper
----------

Purpose of this pattern is to separate functionality away from the View. The important point of using this pattern is to remember to separate the domain from the presentation, as I have done.

For example you'd use if you wanted to output tabular data with alternating rows of colour. In this case, the Template class would have the presentational HTML for this, with the ListHelper class having the logic to decide which HTML to use, from the Template class.

PHP Code:

// controller

$helper = new ListHelper( 'list.template.tpl' );
$helper -> run( $data );
...
$view = new ExampleView( 'template.tpl' );
$view -> add( 'list', $helper -> toHtml() );
...
PHP Code:

class ListHelper {

private $Template;

public function __construct( $filename ) {
$this -> template = new Template( $filename );
}

public function run( $resultset ) {
$html = '';
foreach(
$resultset as $row ) {
$id = $row['id'];
$firstname = $row['firstname'];
$lastname = $row['lastname'];
$html .= $this -> template -> parse( $id, $firstname, $lastname );
}
$this -> template -> setHtml( $html );
}

public function toHtml() {
return
$this -> template -> getHtml();
}
}

class
Template {
private $html = '';
private $fragment;

public function __construct( $filename ) {
$this -> fragment = @file_get_contents( $filename );
}

public function setHtml( $html ) {
$this -> html = $html;
}

public function getHtml() {
return
$this -> html;
}

public function parse( $id, $firstname, $lastname ) {
$temp = ereg_replace( '<id />', $id, $this -> fragment );
$temp = ereg_replace( '<firstname />', $firstname, $temp );
$temp = ereg_replace( '<lastname />', $lastname, $temp );
return
$temp;
}
}

Last edited by Dr Livingston; Apr 2, 2005 at 03:11. Reason: Add the purpose of each pattern
Dr Livingston is offline   Reply With Quote
Old Mar 31, 2005, 07:58   #2
kyberfabrikken
SitePoint Mentor
silver trophy
 
kyberfabrikken's Avatar
 
Join Date: Jun 2004
Location: Copenhagen, Denmark
Posts: 5,916
It would probably be better to use a wiki for that ?
kyberfabrikken is offline   Reply With Quote
Old Mar 31, 2005, 08:01   #3
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Yep, but I have no available web space at the moment
Dr Livingston is offline   Reply With Quote
Old Mar 31, 2005, 09:52   #4
jakuza
SitePoint Enthusiast
 
Join Date: Jan 2005
Location: Rome
Posts: 77
I can donate! I'd be happy to help you with a few Mb!
jakuza is offline   Reply With Quote
Old Mar 31, 2005, 11:17   #5
Super Phil
SitePoint Zealot
 
Join Date: Oct 2004
Location: naperville
Posts: 189
I'd be interested in this as well, webspace and all. I'll set up a wiki on philbrodeur.com if no one minds the domain name If it actually gets going I can set it up as phpwiki.com or whatever.
Super Phil is offline   Reply With Quote
Old Mar 31, 2005, 11:29   #6
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Well, was hoping to keep it to these forums, if the idea took off, and I hope it does, it'd be a sticky thread

I just thought the forums were the best options, from the point of view of topical discussions of these blighters
Dr Livingston is offline   Reply With Quote
Old Mar 31, 2005, 12:28   #7
Andrew-J2000
Currently Occupied; Till Sunda
 
Andrew-J2000's Avatar
 
Join Date: Aug 2001
Location: London
Posts: 2,511
What happened to phppatterns.com, it seems short lived. It would be nice to have a Wiki there (relevant domain), where people can contribute freely, however Harry seems to have disappeared lately?
Andrew-J2000 is offline   Reply With Quote
Old Mar 31, 2005, 12:40   #8
sike
SitePoint Zealot
 
sike's Avatar
 
Join Date: Oct 2002
Posts: 174
why don't you use http://www.wiki.cc/php/Main_Page ?
sike is offline   Reply With Quote
Old Mar 31, 2005, 12:44   #9
jplush76
SitePoint Evangelist
 
jplush76's Avatar
 
Join Date: Nov 2003
Location: Los Angeles, CA
Posts: 484
Quote:
Originally Posted by Andrew-J2000
What happened to phppatterns.com, it seems short lived. It would be nice to have a Wiki there (relevant domain), where people can contribute freely, however Harry seems to have disappeared lately?
I've been wondering that myself...
I've emailed him a couple times but haven't heard anything back and haven't seen anything from him in a while.
jplush76 is offline   Reply With Quote
Old Mar 31, 2005, 13:07   #10
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
PHP Patterns is still there as far as I know, but there has been no fresh content for a long time. Not sure what has happened to Harry but hope it's nothing serious.

As for a Wiki, I hope it doesn't go the same way as PHP Patterns. But where ever this catelog ends up, it has to be easily accessable for everyone, and allow discussions as I see it, as without the discussions, it's all pointless really.
Dr Livingston is offline   Reply With Quote
Old Mar 31, 2005, 14:33   #11
Super Phil
SitePoint Zealot
 
Join Date: Oct 2004
Location: naperville
Posts: 189
The only thing wrong with forums is orginization - each pattern really should have its own thread and then we would need a "master thread".

Edit - I'm not convinced the ratings example is so hot; you're not adding functionality but rather a different type of rating. Is this really the general purpose of the pattern? No offense; I'm still learning some of this stuff.
Super Phil is offline   Reply With Quote
Old Mar 31, 2005, 14:57   #12
davro
SitePoint Enthusiast
 
Join Date: Jun 2004
Location: London
Posts: 66
Hi all,

A while back not to long ago i created a sourceforge project. http://sourceforge.net/projects/standardpattern/
With a simular idea in mind patterns with class examples and unit tests using "Open Source" programming languages php, python, ruby ...
Never actually done anything with the project.

Something along the lines of http://patternshare.org/ would be nice.
This is a cool view of all the patterns at patternshare.org http://patternshare.org/default.aspx...rganizingTable

Note: patternshare.org smells of Microsoft. http://toolbar.netcraft.com/site_rep...tternshare.org

Last edited by davro; Mar 31, 2005 at 15:33.
davro is offline   Reply With Quote
Old Mar 31, 2005, 15:21   #13
sid egg
Massimiliano Bruno Giordano
 
sid egg's Avatar
 
Join Date: Aug 2004
Location: Canada
Posts: 1,490
Obligatory spelling correction:

Catalog.
sid egg is offline   Reply With Quote
Old Apr 1, 2005, 01:51   #14
melancholic
Non-Member
 
melancholic's Avatar
 
Join Date: Nov 2004
Location: Australia
Posts: 455
Obligatory obligatory spelling correction:

Catalogue
melancholic is offline   Reply With Quote
Old Apr 1, 2005, 05:12   #15
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Quote:
I'm not convinced the ratings example is so hot;
I know the example is overly simplistic, but this is what I arrived at whilst understanding this pattern. What the example does however show, is how a Decorator should look like, from the inside point of view. Ie, how a Decorator works internally as I had trouble following what examples have in the past, been posted to these forums

I was actually looking forwards to a lot more enthusiasm for this, and now I'm disappointed that no one else has contributed

Come on gurus, don't be shy, give up your patterns...
Dr Livingston is offline   Reply With Quote
Old Apr 1, 2005, 07:50   #16
Overunner
SitePoint Zealot
 
Overunner's Avatar
 
Join Date: Mar 2004
Location: Sweden
Posts: 180
Ok, here goes nothing:

Data Mapper
PHP Code:

// Base class

class DataMapper
{
  var
$dbh;

  function
insert($data)
  {
    
trigger_error("Abstract call...");
  }

  function
delete($id)
  {
    
trigger_error("Abstract call...");
  }

  function
update($data)
  {
    
trigger_error("Abstract call...");
  }
}

class
ProductMapper extends DataMapper
{
  function
ProductMapper(&$dbh)
  {
    
$this->dbh =& $dbh;
  }

  function
insert($data)
  {
    
/* Application specific, but for example:
        $this->dbh->query("INSERT INTO users VALUE(" . $data['username'] . ", " . $data['password']); */
  
}

  function
delete($data)
  {
    
/* Application specific, e.g:
        $this->dbh->query("DELETE FROM users WHERE id = " . (int) $id); */
  
}

  function
update($data)
  {
    
/* Application specific, e.g:
        $this->dbh->query("UPDATE users SET username = " . $data . " WHERE id = " . $data['id']); */
  
}

  
// Maybe some other functions too...

  
function getAll() {...}

  function
findById($id) {...}

  function
findByFoo($bar) {...}
}
Overunner is offline   Reply With Quote
Old Apr 1, 2005, 07:56   #17
Luke Redpath
SitePoint Guru
silver trophy
 
Luke Redpath's Avatar
 
Join Date: Mar 2003
Location: London
Posts: 796
Quote:
Originally Posted by melancholic
Obligatory obligatory spelling correction:

Catalogue
Obligatory pedantry...

Catalog is the US spelling so could probably be considered acceptable.
Luke Redpath is offline   Reply With Quote
Old Apr 1, 2005, 14:25   #18
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Chain Of Responsibility
---------------------

Using a SWITCH to delegate a decision has it's drawbacks, for example you need to change the script to add another decicion. With the CoR you do not need to change anything, you just add a new handler, provided you adhere to the interface.

Btw, this is the 'classic' GoF (Group of Four) pattern I've posted.

PHP Code:

    // generic handlers

    
$h1 = new Handler1;
    
$h2 = new Handler2;
    
    
// handle a given request
    
$h1 -> setHandler( $h2 );
    
$h1 -> handleRequest( $request );
    
    
abstract class Handler {
        
protected $successor;
        
        
public function __construct() {
        }
        
        
public function setHandler( Handler $successor ) {
            
$this -> successor = $successor;
        }
        
        
abstract public function handleRequest( $request );
    }
    
    class
Handler1 extends Handler {
        
public function __construct() {
            
parent::__construct();
        }
        
        
public function handleRequest( $request ) {
            if(
$request == 'add' ) {
                
// do add
            
} else {
                if(
$this -> successor != NULL ) {
                    
$this -> successor -> handleRequest( $request );
                }
            }
        }
    }
    
    class
Handler2 extends Handler {
        
public function __construct() {
            
parent::__construct();
        }
        
        
public function handleRequest( $request ) {
            if(
$request == 'view' ) {
                
// do view
            
} else {
                if(
$this -> successor != NULL ) {
                    
$this -> successor -> handleRequest( $request );
                }
            }
        }
    }
And another example as requested (below)

PHP Code:

class AdminController extends ActionChainController {

        
public $view;
        
        
public function __construct() {
            
parent::__construct( 'Create' );
            
parent::attach( 'Modify' );
            
parent::attach( 'Remove' );
            
parent::attach( 'Index' );
        }
        
        
public function getView() {
            return
$this -> view;
        }
        
        
public function execute( HttpRequest $req, HttpResponse $res ) {
            
$request = $req -> getParameter( 'cmd' );
            if( empty(
$request ) ) {
                
$request = 'index';
            }
            
$this -> process( $request );
            
            
        }
    }
PHP Code:

interface IAction {

        
public function isSecure();
        
public function getView();
    }
    
    
interface IActionChain {
        
public function attach( $handler );
        
public function process( $request );
    }
    
    
abstract class Action implements IAction {
        
protected $view;
        
        
public function __construct() {
        }
        
        
public function isSecure() {
            return
true;
        }
        
        
public function getView() {
            return
$this -> view;
        }
        
abstract public function execute( HttpRequest $req, HttpResponse $res );
    }
    
    
abstract class ActionChain implements IAction, IActionChain {
        
protected $view;
        
protected $count = -1;
        
protected $handlers = array();
        
        
public function __construct( $handler ) {
            
$this -> handlers[++$this -> count] = new $handler( $this );
        }
        
        
public function attach( $handler ) {
            
$this -> doAttach( new $handler( $this ) );
        }
        
        
public function process( $request ) {
            
$handle = $this -> handlers[0];
            
$handle -> handleRequest( $request );
        }
        
        
public function isSecure() {
            return
true;
        }
        
        
public function getView() {
            return
$this -> view;
        }
        
abstract public function execute( HttpRequest $req, HttpResponse $res );
        
        
private function doAttach( $handler ) {
            
$temp = $this -> handlers[$this -> count];
            
$temp -> setHandler( $handler );
            
$this -> handlers[++$this -> count] = $handler;
        }
    }

Last edited by Dr Livingston; Apr 2, 2005 at 12:06. Reason: Add the purpose of this pattern
Dr Livingston is offline   Reply With Quote
Old Apr 1, 2005, 15:19   #19
davro
SitePoint Enthusiast
 
Join Date: Jun 2004
Location: London
Posts: 66
Basic Patterns:
HttpRequest
FrontController
Controller
---------------------
PHP Code:

class HttpRequest

{
    function
HttpRequest() { }
    
    function
doGet() {
        return
$_GET;
    }
    function
doPost() {
        return
$_POST;
    }
}
PHP Code:

class FrontController extends Controller

{
    function
FrontController() {
        
$Http  = new HttpRequest();
        
$this->setListMerge($Http->doGet(), $Http->doPost());
    }
}
PHP Code:

class Controller

{
    var
$data = array();

    function
Controller() { }    
  
    function
setList($array) {
        
$this->data = $array;
    }  
    function
setListMerge($array1, $array2) {
        
$this->setList(array_merge($array1, $array2));
    }
    function
getList() {
        return
$this->data;
    }    
    function
getListItem($item) {
        return
$this->data[$item];
    }
    function
getListItemByNotation($item, $notation, $rid) {
        
$note = array();
        
$note = explode('.', $this->getListItem($item));
        return
$note[$rid];
    }
    function
isEmpty() {
        if (
count($this->data) <= 0) {
            return
true;
        } else {
            return
false;
        }
    }
}

Last edited by davro; Apr 1, 2005 at 16:49.
davro is offline   Reply With Quote
Old Apr 1, 2005, 17:40   #20
firepages
sitepoint wombat
 
firepages's Avatar
 
Join Date: Jul 2000
Location: Perth Australia
Posts: 1,722
sorry dudes, but without an explanation ( goals, raison d`etre ) of each pattern ... its just gibberish to the uneducated viewer ... & if educated then they dont need the example
firepages is offline   Reply With Quote
Old Apr 2, 2005, 09:11   #21
mx2k
SitePoint Addict
 
mx2k's Avatar
 
Join Date: Jan 2005
Posts: 267
just a suggestion, if you want to do a pattern repository, you might want to include both php4 + php5 examples if possible. alot of people still have to stay within the php4 realm because of shared hosting or other reasons. plus that might add to appeal and help people migrate over to php5 in the long run as well.

and a second suggestion, keep the thread alive, but also maybe use a wiki or repository somewhere else or even another forum attached to this one. that sums up the discussion here, but keep the discussion here cause you never know who might show up with something good to add.

i love the idea Dr. L cause idiots like me have to read books like head first design patterns and professional php5
mx2k is offline   Reply With Quote
Old Apr 2, 2005, 10:49   #22
arborint
SitePoint Wizard
 
Join Date: Aug 2004
Location: California
Posts: 1,672
I certainly like to see code so appreciate the postings. I also to agree firepages that without the simple description, explaining the problem and describing how it works the are not much help. Patterns are not implementations and showing implementations without context reduces the communication value of patterns.

I also have some questions about the patterns listed and I think it is the discussion if the ideas of the pattern that are of value.

For instance, overrunner's Data Mapper look a lot like the Table Data Gateway pattern to me. Table Data Gateway is a fairly simple pattern to describe and give examples of. Data Mapper is considerably more complex and is often implemented used a combination of patterns. In fact several different combinations are possible.

Dr Livingston's is a good example of Chain Of Responsibility. I would be interested in seeing other examples. There was a discussion recently that for the request the Intercepting Filter is a better choice, but for controllers Chain Of Responsibility is the way to go. So showing Chain Of Responsibility combined with the Command pattern would be of interest.

Of davro's patterns only Front Controller is a pattern that I know. I do not think HTTP Request and Controller are patterns (correct me if I'm wrong). And the Front Controller as shown does not seem to dispatch which is a central function of the Front Controller pattern. Though I like the idea of having a base Controller class for Front, Page and Application Controllers.
arborint is offline   Reply With Quote
Old Apr 2, 2005, 12:16   #23
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Quote:
So showing Chain Of Responsibility combined with the Command pattern would be of interest.
I don't think that the combination of these two patterns are of any benifit to either one, as to me there is some conflict? Both patterns work on a Request of some kind, but what I can say is that Command works wonders with a Helper class, much like a ViewHelper.

PHP Code:

// ...

$helper = new RequestHelper( $this -> req ); // pass Request object
$command = $helper -> getCommand();
...
PHP Code:

class RequestHelper {

private $req;

public function __construct( HttpRequest $req ) {
$this -> req = $req;
}

public function getCommand() {
// here you verify command requested, etc
// and return an instance of the class to be executed, as
// in an action
}

private function isCommandFile( $command ) {
if( empty(
$command ) ) {
$command = 'default';
}
return
'commands/'.$command.'.php';
}
}
You may also add other tasks to this Helper in relation to a Request, but it's a fine line to what you put in it. Hope that helps, as I can't see Command and CoR working together, sorry
Dr Livingston is offline   Reply With Quote
Old Apr 3, 2005, 06:33   #24
davro
SitePoint Enthusiast
 
Join Date: Jun 2004
Location: London
Posts: 66
Hi all,

Sorry i can see how my last post might have added some entropy to the domain.
Hopefully this post might help things a little, or maybe not.

Abstract Controller Patterns:
FrontController http://www.martinfowler.com/eaaCatal...ontroller.html
ApplicationController http://www.martinfowler.com/eaaCatal...ontroller.html
PageController http://www.martinfowler.com/eaaCatal...ontroller.html
---------------------
PHP Code:

class FrontController extends Controller

{
    function
FrontController() {
        
$Http  = new HttpRequest();
        
$this->setListMerge($Http->doGet(), $Http->doPost());
    }
}

class
ApplicationController
{
    function
ApplicationController() {
        
$Request  = new FrontController();
        
// do-main
    
}
}

class
PageController
{
    function
PageController() {
        
$Request  = new FrontController();
        
// do-main
    
}
}
Concrete Controller
PHP Code:

class Controller

{
    var
$data = array();

    function
Controller() { }    
  
    function
setList($array) {
        
$this->data = $array;
    }  
    function
setListMerge($array1, $array2) {
        
$this->setList(array_merge($array1, $array2));
    }
    function
getList() {
        return
$this->data;
    }    
    function
getListItem($item) {
        return
$this->data[$item];
    }
    function
getListItemByNotation($item, $notation, $rid) {
        
$note = array();
        
$note = explode($notation, $this->getListItem($item));
        return
$note[$rid];
    }
    function
isEmpty() {
        if (
count($this->data) <= 0) {
            return
true;
        } else {
            return
false;
        }
    }
}
HTTP Handler Object
PHP Code:

class HttpRequest

{
    function
HttpRequest() { }
    
    function
doGet() {
        return
$_GET;
    }
    function
doPost() {
        return
$_POST;
    }
}

Last edited by davro; Apr 3, 2005 at 10:09.
davro is offline   Reply With Quote
Old Apr 15, 2005, 12:51   #25
Dr Livingston
Non-Member
 
Join Date: Jan 2003
Posts: 5,799
Composite
----------

This is a useful pattern to learn where you need to construct hierarchy data structures. The example I've posted below is for an XML file, which I use to construct a web page on the fly, but the hierarchy structure could be from the database. It doesn't matter really.

First, the XML file

Code:
<?xml version="1.0" encoding="utf-8"?>
<fragments>
	<fragment>
		<id>page</id>
		<parent></parent>
		<controller></controller>
		<fragment>
			<id>header</id>
			<parent>page</parent>
			<controller></controller>
		</fragment>
		<fragment>
			<id>body</id>
			<parent>page</parent>
			<controller></controller>
			<fragment>
				<id>partition</id>
				<parent>body</parent>
				<controller></controller>
			</fragment>
		</fragment>
		<fragment>
			<id>footer</id>
			<parent>page</parent>
			<controller></controller>
		</fragment>
	</fragment>
</fragments>
Now the script,

PHP Code:

interface IWalker {

        
public function __construct( $children );
        
public function walk( IParser $parser );
    }
    
    class
XmlWalker implements IWalker {
        
private $children;
        
        
public function __construct( $children ) {
            if( !
$children instanceof DomNodeList ) {
                
throw new IllegalParameterException( 'thrown exception on, expected parameter(s) not found.' );
                break;
            }
            
$this -> children = $children;
        }
        
        
public function walk( IParser $parser ) {
            foreach(
$this -> children as $child ) {
                if(
$child instanceof DomElement ) {
                    
$parser -> push( $child );
                    
try {
                        
$walker = new XmlWalker( $child -> childNodes );
                        
$walker -> walk( $parser );
                    }
catch( IllegalParameterException $e ) {
                        die(
$e -> getMessage() );
                    }
                }
            }
        }
    }

class
ApplicationFaultException extends Exception {
        
public function __construct( $message ) {
            
parent::__construct( $message );
        }
    }
    
    class
IllegalParameterException extends Exception {
        
public function __construct( $message ) {
            
parent::__construct( $message );
        }
    }
    
    
interface IParser {
        
public function getRoot();
        
public function push( $fragment ); // DomElement
    
}
    
    class
XmlAdaptableCompositeParser implements IParser {
        
private $decoratable;
        
        
public function __construct( $decoratable ) {
            
$this -> decoratable = $decoratable;
        }
        
        
public function getRoot() {
            return
$this -> decoratable -> getRoot();
        }
        
        
public function push( $fragment ) {
            if(
$fragment -> nodeName == 'fragment' ) {
                return
$this -> decoratable -> push( $fragment );
            }
        }
    }
    
    class
XmlCompositeParser implements IParser {
        
private $root;
        
        
public function __construct( $fragment ) {
            if( !
$fragment instanceof DomElement ) {
                
throw new IllegalParameterException( 'thrown exception on, expected parameter(s) not found.' );
                break;
            }
            
$this -> root = new Composite( $fragment );
        }
        
        
public function getRoot() {
            return
$this -> root;
        }
        
        
public function push( $fragment ) {
            
$composite = new Composite( $fragment );
            if(
$tmp = $this -> traverse( $this -> getRoot(), $fragment ) ) {
                
$tmp -> attach( $composite );
            }
        }
        
        
private function traverse( $composite, $fragment ) {
            
$parent = $fragment -> getElementsByTagName( 'parent' );
            
$parent = $parent -> item( 0 ) -> nodeValue;
            if(
$composite -> getId() == $parent ) {
                return
$composite;
            } else {
                if(
$composite -> hasChildren() ) {
                    
$children = $composite -> getChildren();
                    foreach(
$children as $child ) {
                        
$c = new Composite( $fragment );
                        if(
$tmp = $this -> traverse( $child, $fragment ) ) {
                            
$tmp -> attach( $c );
                        }
                    }
                }
            }
        }
    }
    
    class
Composite extends Component {
        
public function __construct( $fragment ) {
            if( !
$fragment instanceof DomElement ) {
                
throw new IllegalParameterException( 'throw exception on, expected parameter(s) not found.' );
                break;
            }
            
parent::__construct( $fragment );
        }
    }
    
    
abstract class Component {
        
protected $id;
        
protected $parent;
        
protected $template;
        
protected $children = array();
        
protected $controller;
        
        
public function __construct( $fragment ) {
            
$this -> fragment = $fragment;
            
$this -> id = $this -> getNode( 'id' );
            
$this -> controller = $this -> getNode( 'controller' );
        }
        
        
public function hasChildren() {
            return
count( $this -> children );
        }
        
        
public function getChildren() {
            return
$this -> children;
        }
        
        
public function getNode( $node ) {
            
$items = $this -> fragment -> getElementsByTagName( $node );
            return
$items -> item( 0 ) -> nodeValue;
        }
        
        
public function setTemplate( $template ) {
            
$this -> template = $template;
        }
        
        
public function getTemplate() {
            return
$this -> template;
        }
        
        
public function getController() {
            return
$this -> controller;
        }
        
        
public function getId() {
            return
$this -> id;
        }
        
        
public function attach( $composite ) {
            
$this -> children[] = $composite;
        }
    }
And use it with this for example,

PHP Code:

$dom = new DomDocument;

    
$dom -> load( 'test.xml' );
    
$fragment = $dom -> documentElement -> childNodes;
    
    
$walker = new XmlWalker( $fragment -> item( 1 ) -> childNodes );
    
// require a decorator to filter tags by the name of 'fragment'
    
$parser = new XmlAdaptableCompositeParser( new XmlCompositeParser( $fragment -> item( 1 ) ) );
    
$walker -> walk( $parser );
    
    echo(
'<pre>' ); print_r( $parser -> getRoot() ); echo( '</pre>' );

EDIT: 22/01/06

If you find that you want to apply a number of filters prior to the controller, via a number of decorators, then you could take a what changes I made later;

Note that there is no controller node as such from earlier examples, as the controller it's self is applied as a filter as well.

Code:
...
<fragment>
			<id>header</id>
			<parent>page</parent>
			<filters>
<filter name="PageController" pathname="pathname/to/file" />
<filter name="FilterOneController" pathname="pathname/to/file" />
<filter name="FilterTwoController" pathname="pathname/to/file" />
</filters>
		</fragment>
...
Now, from XmlCompositeParser:ush( $fragment ); you would need to do the following before you add the composite to the composite structure, it's self;

PHP Code:

// ...

$filters = $fragment -> getElementsByTagName( 'filters' );
            
$filters = $filters -> item( 0 );
            
$tmp = array();
            if(
$filters instanceof DomElement ) {
                foreach(
$filters -> childNodes as $filter ) {
                    if(
$filter instanceof DomElement ) {
                        
$name = $filter -> getAttribute( 'name' );
                        
$pathname = $filter -> getAttribute( 'pathname' );
                        include_once(
$pathname.strtolower( $name ).'.php' );
                        
$tmp[] = $name;
                        
                    }
                }
            }
            
$temp = array();
            
$cl = array_shift( $tmp );
            
$temp[] = $cl;
            foreach(
$tmp as $class ) {
                
$temp[] = new $class( array_shift( $temp ) );
            }

$composite = array_shift( $temp ); // holds newly generated decorators
            
if( $tmp = $this -> traverse( $this -> getRoot(), $fragment ) ) {
                
$tmp -> attach( $composite );
            }
For example, here is an example node, and it's output

Code:
...
<filters>
					<filter name="Controller" pathname="controllers/" />
					<filter name="ThirdController" pathname="controllers/" />
					<filter name="SecondController" pathname="controllers/" />
					<filter name="FirstController" pathname="controllers/" />
				</filters>
...
Code:
Array
(
    [0] => FirstController Object
        (
            [decorated:private] => SecondController Object
                (
                    [decorated:private] => ThirdController Object
                        (
                            [decorated:private] => Controller
                        )

                )

        )

)
PHP Code:

class Controller {

        
private $decorated;
        
        
public function __construct() {
        }
        
    }

class
FirstController {
        
private $decorated;
        
        
public function __construct( $decorated ) {
            
$this -> decorated = $decorated;
        }
        
    }

class
SecondController {
        
private $decorated;
        
        
public function __construct( $decorated ) {
            
$this -> decorated = $decorated;
        }
        
    }

class
ThirdController {
        
private $decorated;
        
        
public function __construct( $decorated ) {
            
$this -> decorated = $decorated;
        }
        
    }
The decoration therefore happens automatically for you, just remember though to set the ordering correctly, and it's better to implement a common Interface as well.

Yours, Dr Livingston

Last edited by Dr Livingston; Jan 22, 2006 at 12:44.
Dr Livingston is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread | Next Thread »

Thread Tools
Display Modes

 
Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Sponsored Links
 
Forum Jump


All times are GMT -7. The time now is 19:57.


Powered by vBulletin® Version 3.7.1
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Copyright 1998-2009, SitePoint Pty Ltd. All Rights Reserved