Version: 1.0
Type: Class
Category: Algorithms
License: GNU General Public License
Description: I was using symfony’s sfMixer class. Thought why not create one’s own
This class mixes different object and u can call methods on the component objects (see demo class). Priority can be also assigned to the objects. All the objects in the Mixer are singletons.
///////////////////// /* * Class MyMixer - Mixes objects and provides priority call functionality. * The contents of MyMixer are singletons. * * TODO: implement __call method (done) * : destruction of objects in object store and RMO objects in classMethods (done) * : Constructor data incorporated into objects (done) * : Resetting the Mixer (done) * * * */ class MyMixer { private $objectStore=array(); //contains Component class => their objects private $arrClasses=array(); //an array of classnames to be mixed with their priority value private $classConstructorArg=array(); //an array of classname to the constructor arguments (string) private $classnameParentPriority; //an array of //class id //classname //parent class id //priority private $classMethods; //an array of //components class id, //an array of Method name to corresponding RMO private $flag_everythingready=false; public function resetMixer() { $this->objectStore=array(); $this->arrClasses=array(); $this->classnameParentPriority=array(); $this->classMethods=array(); $this->flag_everythingready=false; } //objects getter methods private function getObjectStoreEntryByClassId($classid) { if(isset($this->objectStore[$classid])) { return $this->objectStore[$classid]; } else throw new Exception('Method MyMixer::getObjectStoreEntryByClassId() provided a non existing key',NOK+6); } private function getObjectStoreEntryByClassName($classname) { $flag=false; foreach($this->classnameParentPriority as $key=>$val) { if($val[0] == $classname) { $flag=true; return $this->getObjectStoreEntryByClassId($key); } } if($flag==false)throw new Exception('Method MyMixer::getObjectStoreEntryByClassName() returned nothing.',NOK+7); } private function createObjectStore() { foreach($this->classnameParentPriority as $key=>$val) { $classname=$this->classnameParentPriority[$key][0]; //make the constructor argument string if(isset($this->classConstructorArg[$classname])) { eval("$this->objectStore[$classid]=new $classname(".$this->classConstructorArg[$classname].");"); } else { eval("$this->objectStore[$classid]=new $classname();"); } } } private function populateclassMethods() { foreach($this->classnameParentPriority as $key=>$val) { $methods=get_class_methods($val[0]); foreach($methods as $method) { $this->classMethods[$key][$method]=new ReflectionMethod($val[0], $method); } } } private function getParents($classname,$level=0) //limits to $level or oldest parent whichever { //comes early if(!class_exists($classname)) { throw new Exception('Wrong Argument to getParents() method. Argument class does not exists.',NOK+5); } elseif($level<0) { throw new Exception('Wrong Argument to getParents() method. Level value is invalid (<0).',NOK+14); } $retarr=array(); $iter=$classname; $i=0; do { $retarr[]=$iter; $i+=1; } while(($iter=get_parent_class($iter)) and (($level==0)?true:(($i==$level)?false:true)) ); return $retarr; } private function populateclassnameParentPriorityArray($includeParents=false) { //populate $id=0; //initial id foreach ($this->arrClasses as $ccname=>$ccpriority) { $parentage=$this->getParents($ccname,($includeParents==true)?0:1); foreach($parentage as $name) { $this->classnameParentPriority[$id++]=array($name,$id,$ccpriority); } $this->classnameParentPriority[$id-1][1]=-1; //set parent to -1 of the last class entered assumingly the super parent } //remove multiple entries of classnames and edit the parent field to correct values $cnames=array(); $lastkey=0; $unsetlist=array(); foreach($this->classnameParentPriority as $key=>$val) { if(!isset($cnames[$val[0]])) { $cnames[$val[0]]=$key; $lastkey=$key; } else { $this->classnameParentPriority[$lastkey][1]=$cnames[$this->classnameParentPriority[$key][0]]; $unsetlist[]=$key; } } foreach($unsetlist as $unsetit) { unset($this->classnameParentPriority[$unsetit]); } //populate the methods array $this->populateclassMethods(); //create object store $this->createObjectStore(); $this->flag_everythingready=true; //everything ready for method invocation on the mixer chk in __call } public function setComponentClasses($argarrClasses,$constructorarg,$setDefaultPriority=false) { $this->classConstructorArg=$constructorarg; $defpriority=1; $argcount=count($argarrClasses); if($argcount == 0) //no entry in arrClasses - its not ok whats the point of mixing { throw new Exception('Argument classes array to MyMixer::setComponentClasses() is empty.',NOK+1); } foreach($argarrClasses as $name=>$priority) { if($priority>=1 and $priority<=$argcount) // 1 <= priority <= count of component classes { if(!in_array($priority,array_values($this->arrClasses))) //no repeated priorities { if(class_exists($name)) { $this->arrClasses[$name]=($setDefaultPriority==false)?$priority:$defpriority++; //put $name=>$priority in $arrClasses } else throw new Exception('Class '.$name.' does not exist in argument to MyMixer::setComponentClasses() method',NOK+2); } else throw new Exception('In argument to MyMixer::setComponentClasses() method you cannot assign same priority ('.$priority.') to two or more than two classes',NOK+3); } else throw new Exception('Invalid priority value ('.$name.' => '.$priority.') passed to MyMixer::setComponentClasses() method',NOK+4); } //now $this->arrClasses contains the component class names and their priority //sort the arrClasses array based on priority only if setDefaultPriority is false //priority values will be unique if($setDefaultPriority == false) { $this->arrClasses=array_flip($this->arrClasses); ksort($this->arrClasses); $this->arrClasses=array_flip($this->arrClasses); } //call private member method $this->populateclassnameParentPriorityArray(); } public function __call($methodname,$arguments) { if($this->flag_everythingready==false) { throw new Exception('Something is missing. Not ready to call methods on the mixer object.',NOK+8); } $narguments=count($arguments); $temparr=explode('____',$methodname); if(strlen($methodname)==strlen($temparr[0])) //if methodname does not contain ____ (4 underscores) { //priority based invocation $namematches=false;$parammatches=false;$called=false; foreach($this->classMethods as $key=>$val) { foreach($val as $mname=>$rmo) { if($mname == $methodname) //name matches { $namematches=true; if($rmo->getNumberOfRequiredParameters() <= $narguments and $narguments <= $rmo->getNumberOfParameters()) { //number of params matches this seems to be the right method to call now $parammatches=true; //gets its object $object=$this->getObjectStoreEntryByClassId($key); $result=null; eval("$result=$object->$methodname(".implode(',',$arguments).");"); $called=true; break; } } } if($called==true) { return $result; } } if($namematches==false) { throw new Exception('There is no method named '.$methodname.'() in the mixer. Call failed.',NOK+9); } elseif($parammatches==false) { throw new Exception('Number of parameters of method '.$methodname.'() in the mixer do not match with the actual call. Call failed.',NOK+10); } } else //classname specific invocation { $classname=$temparr[0];$methodname=$temparr[1];$classexists=false;$methodexists=false;$parammatches=false; foreach($this->classnameParentPriority as $key=>$val) { if($val[0]==$classname) { $classexists=true; $classmethods=$this->classMethods[$key]; if(isset($classmethods[$methodname])) { $methodexists=true; $rmo=$classmethods[$methodname]; if($rmo->getNumberOfRequiredParameters() <= $narguments and $narguments <= $rmo->getNumberOfParameters()) { $parammatches=true; $object=$this->getObjectStoreEntryByClassId($key); $result=null; eval("$result=$object->$methodname(".implode(',',$arguments).");"); $called=true; break; } } } } if($classexists==false) { throw new Exception('The class '.$classname.' does not exists in the mixer',NOK+11); } elseif($methodexists==false) { throw new Exception('The method '.$classname.'::'.$methodname.'() does not exists in the mixer',NOK+12); } elseif($parammatches==false) { throw new Exception('Number of parameters of method '.$classname.'::'.$methodname.'() in the mixer do not match with the actual call. Call failed.',NOK+13); } else { return $result; } } } //destructor method public function __destruct() { $this->resetMixer(); } } ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////// /** * TestingModule actions. */ class Story { protected $title = ''; protected $topic = ''; protected $characters = array(); public function __construct($title = '', $topic = '', $characters = array()) { $this->title = $title; $this->topic = $topic; $this->characters = $characters; } public function getSummary() { return $this->title.', a story about '.$this->topic.' having '.implode(',',$this->characters); } } class Novel extends Story { private $isbn=10; public function __construct($title,$topic,$chars) { parent::__construct($title,$topic,$chars); } public function setISBN($isbn=0) { $this->isbn=$isbn; } public function getISBN() { return $this->isbn; } public function getSummary() { echo parent::getSummary(); } } class Book { protected $isbn = 1000; public function setISBN($isbn = 0) { $this->isbn = $isbn; } public function getISBN() { return $this->isbn; } } class TestingModule { /** * Executes index action * */ public function executeTestAction() { try { $mixer=new MyMixer(); $mixer->setComponentClasses(array("Novel"=>1),array( "Novel" => ""alladin","alladin's escape",array("alladin","genie","jasmine")" )); echo $mixer->getSummary(); } catch(Exception $e) { echo "<br>Caught exception ".$e->getMessage()." <br>"; } } }