HACK : Mixin in PHP5

Discussion in 'Ruby' started by zimba.tm@gmail.com, Jan 6, 2005.

  1. Guest

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

    ?>
     
    , Jan 6, 2005
    #1
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Willy Kreim
    Replies:
    15
    Views:
    683
    Dale King
    Apr 15, 2006
  2. Skeets
    Replies:
    4
    Views:
    6,020
    Skeets
    Feb 23, 2006
  3. Jeff

    php5 or asp.net

    Jeff, Jun 26, 2006, in forum: ASP .Net
    Replies:
    4
    Views:
    555
    =?Utf-8?B?V2ViQnVpbGRlcjQ1MQ==?=
    Sep 7, 2006
  4. Replies:
    2
    Views:
    17,446
    IchBin
    Aug 15, 2006
  5. romiro

    PHP5 programmer learning Python

    romiro, May 27, 2007, in forum: Python
    Replies:
    5
    Views:
    306
    =?iso-8859-1?q?Luis_M=2E_Gonz=E1lez?=
    May 28, 2007
Loading...

Share This Page