Mixins is a known multiple inheritance concept from languages like Python or Ruby or (but in a different way in JavaScript). In Ruby you can include another class definition, in Python you extend from multiple classes and in JavaScript you use prototype to copy methods from one class to another. In PHP there is no default strategy how to reach that, but nevertheless multiple inheritance leads often to the advanced usage of the mudclump pattern, sometimes it is practically. Think on PHPUnit and its assert*()-methods. They are defined in PHPUnit_Framework_Assert. PHPUnit_Framework_TestCase is derived from the last but it would be much nicer to have the possibility here to mixin my custom assertions. For example I have a custom assertion to ensure a certain object implements a valid singleton. Currently I need to patch PHPUnit, but why should I need to?
I am currently working on a half-automized storage component, which provides helper functions for database queries (auto-generate a WHERE-clause from a filter object, create a field list out of a dependency list and stuff like that). I do not want to pollute my class hierarchy but I want to have them pluggable and I want my collegues to add their own. So here is my naive approach (only works for methods).
class Mixer
{ protected $_includes = array(); public function includeClass($include) { if (in_array($include, $this->_includes)) { throw new Exception( sprintf(‘Include “%s” already added’, $include) ); } $this->_includes[] = $include; $this->_extractSymbols($include); return $this; } public function getIncludes() { return $this->_includes; } protected function _extractSymbols($include) { foreach (get_class_methods($include) as $method) { if (isset($this->_symbols[$method])) { throw new Exception( sprintf( ‘Include “%s” already exported from “%s”’, $method, $this->_symbols[$method] ) ); } if (!substr($method, 0, 1) !== ‘_’) { $this->_symbols[$method] = $include; } } } public function __call($method, $arguments) { if (substr($method, 0, 1) === ‘_’) { throw new Exception( sprintf(‘You are not allowed to call “%s”’, $method) ); } if (!isset($this->_symbols[$method])) { throw new Exception( sprintf(‘Method “%s” is not provided’, $method) ); } else { $include = $this->_symbols[$method]; $arguments[] = $this; return call_user_func_array( array($include, $method), $arguments ); } } }class Mixin
{ public static function mix($string, Mixer $mixer) { echo “Hello, I’m mixed in\n”; }
}class MyMixer extends Mixer
{ public function __construct() { $this->includeClass(‘Mixin’); }
}
Now, I can access Mixing::mix() from MyMixer. A better approach somewhere?
Filed under JavaScript, Mixins, PHP, Python, Ruby & three comments & no trackbacks
Trackback specific URI for this entry
Gerd Riesselmann reckons:
published on September 14th 2007, 01:23:28 pmThere are other problems with this approach, for example instance_of() doesn’t work, too… So any client using a Mixer must be aware of that, which in turn requires knowledge. There is another name for that kind of knowledge and it’s "Tight coupling".
If a programming language should allow multiple inheritance or not is well discussed for several years, and most languages do not support it, due to the inner complexity of this issues. One of these issues are functions of the same name. Your implementation decided to throw an exception in that case, which is – well – bad, since you application may break, if you change one of the classes that get mixed in: Tight coupling again (the super class must be aware of all classes that mix it in).
Languages that do not support multiple inheritance, like PHP, C#, and many other, usually offer another concept to achieve something similar: interfaces. Interface based design is a special kind of object oriented design with its own pros and cons. However it works extremely well with the usual design patterns.
Reply
Lars Strojny supposes:
published on September 14th 2007, 02:21:07 pmOf course I could add an interface for this mixins to let them export there method-lists by themselves.
Something like
interface Mixin_Interface
{ /** * @return array List of exported methods */ public function getMethods();
}
Class Mixing could implement Mixin_Interface and returns its list of exported methods. But the theoretical concept of moving static methods into the Mixer class scope would not change (while thinking about, it would be more elegant to use an interface here, point for you).
Reply
FractalizeR replys:
published on November 7th 2010, 09:46:32 pmJust finished my small PHP mixin library implementation that allows to use both inheritance and composition to introduce mixin functionality.
Reply