/ Published in: PHP
This is a set of classes that work with each other to make a lazy object loader factory.
Expand |
Embed | Plain Text
Copy this code and paste it in your HTML
<?php /* This code implements a sort of super late binding using a factory and a facade. The factory creates facade objects which are just wrappers around the real object. When I ask the factory for an object it gives me the facade. Only when I try to access the object methods does it create the actual object. The facade behaves (or is supposed to) the same as the object it is wrapping. One shortcoming of this implementation is that it only intercepts method calls. Attempts to access member variables will fail. Need to implement __get and __set to handle this. */ /* * I made a simple log/output class so I don't use echo/print * in the actual code. */ static function head($s){ echo "------ $s ------\n"; } static function debug($s){ } static function out($s){ } static function br(){ echo "--------------\n"; } } /* * A wrapper around an actual class. * Will pass through all functions and variables * as if it is the real underlying object. */ class Facade { private $object = null; private $wrappedClass; private $constructorArgs; private $properties; public function __construct($cls, $args = null){ $this->wrappedClass = $cls; $this->constructorArgs = $args; } // private function makeDynamicObjectInstance(){ if ( ! $this->object ){ if ( ! $this->constructorArgs ){ $this->object = new $this->wrappedClass(); } else{ $reflect = new ReflectionClass($this->wrappedClass); $this->object = $reflect->newInstanceArgs($this->constructorArgs); } foreach($this->properties as $varname => $value){ $this->object->$varname = $value; } } } } // I have to make this funciton public to allow the factory to set it if needed. public function facade_setProperties($properties){ $this->properties = $properties; } public function __call($name, $args){ if ( ! $this->object ) $this->makeDynamicObjectInstance(); trigger_error('Call to undefined method ' . $this->wrappedClass . '::' . $name . '() in ' . $trace[1]['file'] . ' on line ' . $trace[1]['line'], E_USER_ERROR); } } public function __get($name){ if ( ! $this->object ) $this->makeDynamicObjectInstance(); return $this->object->$name; // FIXME: will crash if doesn't exist } public function __set($name, $val){ if ( ! $this->object ) $this->makeDynamicObjectInstance(); $this->object->$name = $val; } } class Factory{ /* I am telling the factory what objects I want it to make for me. I can put any objects in here I want. They do not need to extend any base class. */ // I can add any properties I want 'myvar1' => 'myvar1-value', 'temp' => '56', 'humidity' => 53, ) ), ), ), ), 'length' => 6.5, 'status' => 1, 'sex' => 'm', 'weight' => 150, ) ), ), ), ), ), ), ); // I will call this method when I want an object // I don't have to care if it is instantiated yet. // FIXME: calling this with a name that isn't in the factory will fail. static function getObject($name){ $class = self::$def[$name]['class']; self::$objects[$name] = new Facade($class, self::$def[$name]['constructorArgs']); } else{ self::$objects[$name] = new Facade($class); } foreach (self::$def[$name]['properties'] as $key => $value) { // is it a reference to self? if ( $value['@ref'] == $name ){ $props[$key] = self::$objects[$name]; } else{ $props[$key] = self::getObject($value['@ref']); } } else $props[$key] = $value; } self::$objects[$name]->facade_setProperties($props); } return self::$objects[$name]; } } class Simple{ // a simple class to demonstrate classes with no constructor public $var = 'something'; } // Now I will make some classes to play with the factory/facade abstract class Person{ protected $name = null; public $sex; private $car = null; public $weight; public function __construct($name = null, $carId=null){ $this->name = ($name) ? $name : null; $this->car = ( $carId ) ? Factory::getObject($carId) : null; } public function car(){ if ( $this->car ) else } public function outName(){ if ( $this->name ) else } public function getName(){ //Log::debug('Returning : '.$this->name); return $this->name; } } class Greek extends Person{ public function __construct($name){ parent::__construct($name); } function outClassName(){ } } class Egyptian extends Person{ protected $enemy; public function __construct($name){ parent::__construct($name); $this->enemy = Factory::getObject('memnon'); } function attack($s){ } function enemy(){ //Log::debug(print_r($this->enemy, true)); $enemyName = ($this->enemy) ? $this->enemy->getName() : 'no enemy'; } } class Friend extends Person{ public $length; public $status; public function __construct($name){ parent::__construct($name); } public function outStats(){ } } class Car{ private $color; public function __construct($color){ $this->color = $color; } function color(){ return $this->color; } function getColor(){ return $this->color(); } } $memnon = Factory::getObject('memnon'); $achilles = Factory::getObject('achilles'); $car1 = Factory::getObject('car1'); $imhotep = Factory::getObject('imhotep'); $simple1 = Factory::getObject('simple1'); $simple3 = Factory::getObject('simple3'); $memnon->outName(); $car1->color(); $imhotep->enemy(); //the following will fail //Log::out('myvar1 = '.$simple3->myvar2); // Let's put it in a non-factory object class Plain{ public $friend; public function __construct(){ } public function setFriend($name){ $this->friend = Factory::getObject($name); } public function outFriendName(){ } } $plain = new Plain(); $plain->setFriend('jesse'); $plain->outFriendName(); $friend = $plain->friend; $friend->outStats();