HACK : Mixin in PHP5

Z

zimba.tm

Hi,

I've done some PHP hacking after reading all the ruby doc.
The implemented thing is a form of mixin, with all limitations PHP has.

Let me know if I have understood the concept, otherwise it's just for
your
curiosity ;)

Cheers,
zimba

----------------
<?php
/*
* AUTHOR : Created on 5 janv. 2005 by Jonas Pfenniger
* LICENSE : You are free to use this code
* DESC : Mixins are pretty similar to multiple inheritance. The
goal is to
* add capabilites from a class to another class or instance.
* I don't know what it's usefull for, but I'm sure you'll find out ;)
* Because of the limitations of the PHP language, I was not able to do
some
* things, but it was fun to find workarouds where it's possible..
*
* This hack was inspired by the Ruby language, who supports mixins
natively
* See: http://www.ruby-lang.org and http://www.rubyonrails.com
*
*/



/**
* Mixin : allow to inheritate methods and properties of multiple
objects
* - Limitations :
* - Cannot inherit __get and __set
* - Reflection does not show the new methods
* - Some behaviors are still weird
*/
class Object
{
/**
* Contains class_name => instances
*/
private $mixin_objects = array();

/**
* Contains method_name => calling code
*/
private $mixin_methods = array();

/**
* For the tests
*/
public $hoi = 0;

/**
* Method and variable mixing
* @var string A class name
*/
public function mixin($class_name)
{
if (!class_exists($class_name))
trigger_error("Class name $class_name is not loaded", E_USER_ERROR);
if (array_key_exists($class_name, $this->mixin_objects))
{
trigger_error("Mixin $class_name allready registered");
return;
}

// Variable argsnum on constructor
$args = func_get_args();
$c = 'return new '.$class_name.'(';
for($i=1; $i<count($args); $i++)
{
$c .= '$args['.$i.']';
if ($i < count($args) - 1) $c .= ',';
}
$c .= ');';

// Create instance
$x = eval($c);
if ($x instanceof MixinChild)
{
$x->setMixinParent($this);
}
$this->mixin_objects[$class_name] = $x;

// Link methods
$refl_class = new ReflectionClass($class_name);
$refl_methods = $refl_class->getMethods();
foreach($refl_methods as $refl_method)
{
// TODO : Inherited methods should not be added

// Do not private and protected methods
if ($refl_method->isPublic())
$this->mixin_methods[$refl_method->getName()] =
'$this->mixin_objects['.
$class_name .
']->'. $refl_method->getName();
}

// Link parameters
foreach ($x as $k => &$v)
{
if (!isset($this->$k))
$this->$k = &$v;
}
}

/**
* Method overloading
* @var string Method name
* @var array Method arguments
*/
public function __call($method_name, $args)
{
if (!array_key_exists($method_name, $this->mixin_methods))
{
trigger_error("Method $method_name does not exist");
return;
}

$c = 'return '. $this->mixin_methods[$method_name] .'($args[0]';
for($i=1; $i<count($args); $i++) $c .= ',$args['.$i.']';
$c .= ');';

return eval($c);
}

public function hasMixin($class_name)
{
return array_key_exists($class_name, $this->mixin_objects);
}

/**
* For the tests
*/
public function directCall()
{
return;
}
}

/**
* Use this class if you want the mixed class to have access to the
parent
*/
abstract class MixinChild extends Object
{
protected $mixin_parent = null;

public function setMixinParent(Object $mixin_parent)
{
$this->mixin_parent = $mixin_parent;
}

}

/**
* Implementation example
*/
class Prout extends MixinChild
{
public $woot = 2;

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

/**
* Woot is so cool
*/
public function Woot($a, $b, $c)
{
return "$a, $b and $c are reading /.";
}

public function get()
{
return $this->woot;
}

public function set($x)
{
$this->woot = $x;
$this->mixin_parent->hoi = $x;

}

public function indirectCall()
{
return;
}

public function __set($k, $v)
{
$this->mixin_parent->$k = "PHP";
}

public function __get($k)
{
return "I love ".$this->mixin_parent->$k;
}
}

/***\
|***|==> Start demo code HEHE
\***/

define('BR', "<br />\n");


echo "<h3>Mixin demo</h3>";


$x = new Object();

// Constructor assignation
$x->mixin('Prout', 6);
echo "Constructor test : ". ($x->woot==6?'true':'false'), BR;

// Settest
$x->set(5);
echo "Set test: " . ($x->woot==5?'true':'false'), BR;
echo "Set parent test: " . ($x->hoi==5?'true':'false'), BR;

// Gettest
$x->woot = 3;
echo "Get test: ". ($x->get()==3?'true':'false'), BR;

// Call test
echo "Method call test: " . $x->Woot('nitro', 'tritoul', 'zimba'), BR;


// __get and __set test
$x->notAssignedVar = 3;
echo "__set and __get test : " . ($x->notAssignedVar == 'I love
PHP'?'true':'false'), BR;





//********** BENCHMARKS ***********

$loops = 10000;

// Direct call
$start_time = (float) array_sum(explode(' ', microtime()));
for($i=0; $i<$loops; $i++)
{
$x->directCall();
}
$end_time = (float) array_sum(explode(' ', microtime()));
$direct_time = $end_time - $start_time;
echo "Direct call bench($loops) : ". $direct_time, BR;

// Mixin call
$start_time = (float) array_sum(explode(' ', microtime()));
for($i=0; $i<$loops; $i++)
{
$x->indirectCall();
}
$end_time = (float) array_sum(explode(' ', microtime()));
$mixin_time = $end_time - $start_time;
echo "Mixin call bench($loops) : " . $mixin_time, BR;

// Difference
echo "Execution time difference : ".$mixin_time / $direct_time, BR;


echo "<b>the end</b>", BR;
echo "<pre>";
print_r($x);
echo "</pre>";

?>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,190
Members
46,736
Latest member
zacharyharris

Latest Threads

Top