Scriptorama.nl

Header image showing a keyboard, mouse, laptop and books on design patterns

Re: mixins in PHP

We don't normally post in english, but as I want to reply to a posting by Ivo Jansch (who has been around Scriptorama a few times) which is also in english, I'll make an exception.

In his posting "Mixins in PHP", Ivo describes a way to "copy" methods from certain classes into a specific class of his choosing. This allows him to pick certain methods from a class, and it allows him to specify this during runtime rather than "compile-time". He does this by using the new __call() magic method in PHP5 which allows him to intercept any calls to methods that do not exist in the current class.

He runs into a few problems though, one not being able to call the object without using eval() and another rather important problem is that he has no access to the scope of the object from the "mixed in methods" what, I feel, is sort of defeating the whole purpose of the mixin.

There is a different way though, and we've visited it on Scriptorama before.

While I have some second thoughts on the usefulness of mixins (care to enlighten me, Ivo?), like I said, I do know of another way that allows for better performance by eliminating the use of eval() and also allows the method to use the scope of the object. That way is using the runkit extension developed by Sara Golemon. The runkit extension allows you to, amongst other things, actually copy the methods from one class to another. Here's a modification of Ivo's Object class, using the runkit extension:

PHP:
  1. class Object
  2. {
  3.     function __construct()
  4.     {
  5.         foreach ($this->mixins as $mixin)
  6.         {
  7.             $methods = get_class_methods($mixin);
  8.            
  9.             if (is_array($methods)) {
  10.                 foreach ($methods as $method)
  11.                 {               
  12.                     runkit_method_copy('Object', $method, $mixin, $method);
  13.                 }
  14.             }
  15.         }
  16.     }
  17. }

By actually copying the method (I imagine this involves copying the actual internal Method datastructure into the class definition) we now have the method in the class and technically the method can access the entire class aswell, as shown here:

PHP:
  1. class Alertable
  2. {
  3.     function blink()
  4.     {      
  5.         echo $this->Name;
  6.         echo "Blink!";
  7.     }
  8. }
  9.  
  10. class Hello extends Object {
  11.     var $mixins = array ("Alertable")
  12.     var $Name = "Scriptorama";
  13.  
  14.     function toString()
  15.     {
  16.         return "Hello, world";
  17.     }
  18. }
  19.  
  20. $o = new Hello();
  21. $o->blink();

Except that, well.., it doesn't. That's not entirely true. It appears runkit isn't as stable as we'd like it to be and it crashed and burned repeatably when used in the above scenario, taking apache with it. This was on PHP 5.1.4. Not accessing $this in Alertable helped a bit meaning I could actually get it to run the blink method at times, but it would crash many times in between. The runkit extension could use a little love, I suppose.

Fun stuff, though :)

Reageer ook!

(ik reageer in het nederlands, aangezien de meeste lezers hier ws. nederlands kunnen lezen ;-))

'another rather important problem is that he has no access to the scope of the object from the "mixed in methods".'

Als je daarmee bedoelt dat de methode geen toegang heeft tot zaken als $this, dat heeft ie wel; dat komt juist door het statisch aanroepen van de methode (waar ik de eval voor nodig had).

Daar heb ik toch even straal over heen gelezen :)

Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>