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>";
}
}
}