Nov 08 2010

Practising OOP in PHP5: emulating mixins (Ruby-like)

Category: Articles,PHPFractalizeR @ 12:25 am

Some evening I was implementing behavorial patterns in my framework’s ORM and I desperately needed something like Ruby mixin or C# extension method or like trait/graft in PHP6+. So I was curious to see how can I implement mixins in PHP. If you don’t know what mixin is, I will tell you at once.

So, I invite you to follow me on paths to implementing mixins in PHP and programming a small library to support them. This article targets beginner and average level programmers. Just be sure you know what OOP is. During the process, I will also make some small mistake about nuances of PHP 5.3 work with classes, will point to it and suggest my solution to it. Good reading!

What is a mixin?

Mixin is a class, providing it’s methods and properties to other classes. You can consider mixing other classes into a class as an emulation of multiple inheritance which is not implemented in PHP. Let me show you a small pseudo-code example to clarify what I mean:

<?php
mixin Timeable {
private $timeStarted;
private $timeStopped;
public function start() { $timeStarted = time(); }
public function stop() { $timeStopped = time(); }
public function getElapsed() { return $timeStopped - $timeStarted }
}
 
mixin Dumpable {
public function dump() { var_dump($this); }
}
 
class MyClass mixing Timeable, Dumpable {
public function hello() {}
}
 
$a = new MyClass();
$a->start();
sleep(250);
$a->stop();
echo $a->getElapsed();
$a->dump();
?>

Got it? Mixins just add their own implementation into the class like this class is inherited from them all. They can manipulate class instance they are mixed into. So, this is the functionality we will implement today.

The task.

  • We have to implement a possibility to mix some classes functionality into other classes
  • Mixins should not be loaded before the class, that mixes them in. In the example at the top, I used pseudo-syntax, that allowed us to define mixin classes just at class declaration. But such approach has it’s downsides. What if we want plugins to mix some functionality into existing classes of our system? In this case we could register mixins somewhere early enough in code and it’s important not to load classes at once.
  • If we mix something into the class, that means, we mix this into all his descendants also. This is logical, since we are at OOP here.
  • It is desired to be performance-wise while implementing the system especially when we have many mixins in the system.
  • The modification of existing system classes should not require us to change system architecture to start using mixins. That means, we need to support some other system besides inheritance to teach classes to mix themselves into others.
  • Public methods and properties of mixins should be accessible via owner class interface (Referred as “Aggregator” further in the article because it can aggregate the functionality of several mixins into himself). And private and protected ones should be visible to mixin only.
  • Mixin should have access even to private and protected properties of the aggregator (inventing this requirement, I followed Ruby a little. There is no hidden or protected properties in Ruby in the sense we have them in PHP or C#. All interface members are public. But because mixin should be able to introduce new behavior into the class, it might need aggregator protected member access.

Architecturing Registry class.

Let’s think a little. We might want to add different mixins to different system classes. So, we should store information somewhere about what mixins are mixed into what classes. This information is global and should be publicly accessible from everwhere. So, I chose static class to implement this (PHP has no notion about static classes like C#, so saying “static class” I just mean the class, which is not intended to be instantiated because all it’s functionality is exposed via static members, accessible using class name). To make a small task for you, if you will be interesting and after you finish reading this article to change the registry to avoid singletons at all.

It follows, that the registry should be able to register mixins as being used in certain aggregator classes. And also we said, that if we register a mixin for some class, it’s functionality should also go to all descendant classes. We cannot get a list of ancestor classes during registration because the inspection of class hierarchy will require class loading, we would like to avoid. This leads us to building a list of association classname => mixin list should be build afterwards. Also, this list should be cached to avoid rebuilding again and again.

<?php
class Registry {
	private static $registeredMixins = array();
 
	public static function register($className, $mixinClassName) {
		$mixinClassNames = func_get_args();
		unset($mixinClassNames[0]);
 
		foreach ($mixinClassNames as $mixinClassName) {
			self::$registeredMixins[$className][] = $mixinClassName;
 
		}
 
		self::$classNameToMixinCache = array();
	}
}

Registration function turned out to be easy. Just just pass aggregator class name to it and the list of mixins for it. A list of supported mixins you can provide using comma as a separator. func_get_args() will do the magic for us (add an elegant support of providing a list of mixins as an array to the registration function, if it’s of any interest for you). And then we just add each mixin into the list of mixins for a given class. The last call at the end of the function invalidates out cache because mixin registration for a given class adds this mixin to all descendant classes as well and that in turn requires cache to be rebuilt.

Ok. Let’s write caching function. It should walk a list of classes and mixins for them and add mixins into all descendants of the given class. This is cache.

To implement it, we need a function to get a list of ancestors for a given class:

<?php
private static $classNameToMixinCache = array();
 
	private static function getAncestors($className) {
		$classes = array($className);
		while (($className = get_parent_class($className)) !== false) {
			$classes[] = $className;
		}
		return $classes;
	}
 
	private static function precacheMixinListForClass($className) {
		if (isset(self::$classNameToMixinCache[$className])) {
			return;
		}
 
		$ancestors = self::getAncestors($className);
		$result = array();
		foreach ($ancestors as $ancestor) {
			if (isset(self::$registeredMixins[$ancestor])) {
				$result = array_merge($result, self::$registeredMixins[$ancestor]);
			}
		}
		self::$classNameToMixinCache[$className] = array_unique($result);
	}

Please pay attention, that this function builds cache of mixins only for a given single class name. We will not build the whole cache at once because the most part of it will be of no use for us during normal application work. Before building something, let’s check, if it’s already done.

Now if we need to get a mixing list for a given class from, we can use such a function:

<?php
	public static function getMixinsFor($className) {
		self::precacheMixinListForClass($className);
		return self::$classNameToMixinCache[$className];
	}

Let’s move on. Imagine, we call a method on aggregator, which is not defined in it, but in some mixin. What should be do? We need to get a list mixins for this class, walk it and see, if any of the mixins define a method we need.

The mixin is a class itself, so we do this:

<?php
private static $methodLookupCache = array();
 
	public static function getMixinNameByMethodName($className, $methodName) {
		if (isset(self::$methodLookupCache[$className][$methodName])) {
			return self::$methodLookupCache[$className][$methodName];
		}
 
		self::precacheMixinListForClass($className);
 
		foreach (self::$classNameToMixinCache[$className] as $mixin) {
			if (method_exists($mixin, $methodName)) {
				self::$methodLookupCache[$className][$methodName] = $mixin;
				return $mixin;
			}
		}
		throw new MemberNotFoundException("$className has no mixed method $methodName()!");
	}

If there is a record in cache for a given class name and method name, we just return them. If not – we make a list of mixins for a given class, walk them and check if some implements needed method. If yes, we add it to cache and return mixin name. If not – we throw an exception.

The case with properties is exactly analogous so I invite you to write an implementation for it yourself.

Ok. That’s all for the registry. Let’s move to programming mixin class.

Programming mixin.

So, the mixin. What’s wrong with it?It’s just a class. It’s different in that it can work with the fields of another class instance. And this instance should be passed to mixin in the constructor, right?

<?php
class Base {
	protected $_owningClassInstance;
 
	protected $_owningClassName;
 
	public function __construct($owningClassInstance) {
		$this->_owningClassInstance = $owningClassInstance;
		$this->_owningClassName = get_class($owningClassInstance);
	}
}

I called the base class for mixins Base just because in mu project it belongs to the Mixins namespace and naming it something more concrete is not required. But you can name it whatever you want.

We can work with public methods and properties in a regular way using $owningClassInstance variable. But for accessing private and protected fields we should use reflection.

<?php
protected $_owningPropertyReflectionCache;
 
	protected $_owningMethodReflectionCache;
 
	protected function getProtected($name) {
		if (! isset($this->_owningPropertyReflectionCache[$name])) {
			$property = new \ReflectionProperty($this->_owningClassName, $name);
			$property->setAccessible(true);
			$this->_owningPropertyReflectionCache[$name] = $property;
		}
		return $this->_owningPropertyReflectionCache[$name]->getValue($this->_owningClassInstance);
	}
 
	protected function setProtected($name, $value) {
		if (! isset($this->_owningPropertyReflectionCache[$name])) {
			$property = new \ReflectionProperty($this->_owningClassName, $name);
			$property->setAccessible(true);
			$this->_owningPropertyReflectionCache[$name] = $property;
		}
		$this->_owningPropertyReflectionCache[$name]->setValue($this->_owningClassInstance, $value);
	}
 
	protected function invokeProtected($name, $parameters) {
		$method = new \ReflectionMethod($this->_owningClassName, $name);
		$method->setAccessible(true);
		$parameters = func_get_args();
		unset($parameters[0]);
		$method->invokeArgs($this->_owningClassInstance, $parameters);
	}

Pay attention, that I used caching again here t0 avoid recreation of instances of base reflection classes. To reduce memory consumption you can totally skip caching here.

Someone already noticed, that method_exists() and property_exists() functions we used in registry class check mixin for having also private and protected members along with public. Because of that, aggregator may try to call some function on mixin with the name of private or protected function. We’ll get the error anyway, but I prefer to do that explicitly:

<?php
public function __call($name, array $arguments) {
		throw new MemberNotFoundException(
				"Method $name is not defined or is not accessible in mixin \"" . get_class() . "\"");
	}
 
	public function __get($name) {
		throw new MemberNotFoundException(
				"Property $name is not defined or is not accessible in mixin \"" . get_class() . "\"");
	}
 
	public function __set($name, $value) {
		throw new MemberNotFoundException(
				"Property $name is not defined or is not accessible in mixin \"" . get_class() . "\"");
	}

As a small task I invite you to try to fix this awkward behavior of our registry class. BTW, such behavior will lead to impossibility to call a public method of a mixin on the aggregator, if it has a name previously registered because it was private or protected.

So… That’s it. Mixin is ready. Just one step more and we are good – implementing aggregator.

Programming aggregator class.

What aggregator can do? It can store instances of mixin classes and call their methods. Well, access their properties as well. We’ll implement such behavior using PHP “magic” methods.

<?php
class Aggregator {
 
	protected $_mixins;
 
	protected $_className;
 
	public function __construct($aggregatorClassInstance = false) {
		$this->_className = $aggregatorClassInstance ? get_class($aggregatorClassInstance) : get_class($this);
		$mixinNames = Registry::getMixinsFor($this->_className);
		foreach ($mixinNames as $mixinName) {
			$this->_mixins[$mixinName] = new $mixinName($aggregatorClassInstance ? $aggregatorClassInstance : $this);
		}
	}
}

In the constructor code we just get a list of the mixins for a given class and then we walk it to create mixin class instances.

Variable $aggregatorClassInstance is intended to be used to skip Aggregator class inheritance necessity. Using it, we will be able to include Aggregator class into some other class and call it’s constructor with $aggregatorClassInstance set to this parent class. Doing this will get us a mixin list for this owning class.

If an explanation looks complicated, skip it. Just scroll down to see simple examples of such approach. Check Composition example and it’s differences from Inheritance one.

Magic methods implementation:

<?php
public function __call($name, array $arguments) {
		return call_user_func_array(array($this->_mixins[Registry::getMixinNameByMethodName($this->_className, $name)], 	$name), $arguments);
	}
 
	public function __get($name) {
		return $this->_mixins[Registry::getMixinNameByPropertyName($this->_className, $name)]->$name;
	}
 
	public function __set($name, $value) {
		$this->_mixins[Registry::getMixinNameByPropertyName($this->_className, $name)]->$name = $value;
	}
 
	public function __isset($name) {
		return isset($this->_mixins[Registry::getMixinNameByPropertyName($this->_className, $name)]->$name);
	}

Each magic method requests information from our registry. Simple. Even the exception class:

<?php
class MemberNotFoundException extends \Exception {}

Some examples

First check traditional inheritance approach:

<?php
class MixinAggregatorSample extends Mixins\Aggregator {
 
}
 
class MixinHello extends Mixins\Base {
 
	protected $inaccessible;
 
	public $text = "I am a text!\r\n";
 
	public function hello() {
		echo ("Hello from mixin!\r\n");
	}
}
 
Mixins\Registry::register("MixinAggregatorSample", "MixinHello");
 
$a = new MixinAggregatorSample();
$a->hello(); //Accesing mixed methid
echo ($a->text); //Accessing mixed property
$a->text = "I am also a text!\r\n"; //Setting mixed property
//$a->inaccessible = 'Error here'; //Throws exception
//$a->inaccessible2 = 'Error here'; //Throws yet another exception (Homework: explain, why)
echo ($a->text);
var_dump(isset($a->text));

And now have a look to a composition one:

<?php
class MixinAggregatorSample {
 
	protected $_aggregator;
 
	public function __construct() {
		$this->_aggregator = new Mixins\Aggregator($this);
	}
 
	public function __call($name, $arguments) {
		return $this->_aggregator->__call($name, $arguments);
	}
}
 
class MixinHello extends Mixins\Base {
 
	public function hello() {
		echo ("Hellp from mixin!");
	}
}
 
Mixins\Registry::register("MixinAggregatorSample", "MixinHello");
 
$a = new MixinAggregatorSample();
$a->hello();

See the difference? In the case with composition we are free to inherit aggregator class from whatever class we want without loosing functionality. Of course, all magic methods needs to be implemented, not just __call();

Performance

I made some dumb profiling. They are too stupid, done on my home machine with IDE opened, WinAmp playing etc.

Time native: 0.57831501960754
Time byname: 1.5227220058441
Time mixed: 7.5425450801849
Time reflection: 12.221807956696
  • Native – timing of PHP native method call
  • Byname –  timimg of method call using method name $myClass->$methodName
  • Mixed – timing of mixed method call
  • Reflection – timing of mixed method call, that changes protected class property using Reflection. E.g.= mixed + reflection.
  • Timing is seconds per 800.000 calls.

I think demonstrated performance is enough for this approach to be used in a big project. Usually mixed methods are not called thousands of times in a script and 10 microseconds for a mixed method call instead of 0.7 microseconds for native is acceptable. Especially if we measure DB query execution timing or htmlspecialchars() call on a large amount of text which is far more than even 100 microseconds.

Since we use PHP hashed arrays, an increase in the number of mixins and aggregator classes should not affect performance too much.  Though, If you make some tests, I would be very grateful.

Epilogue

I will be very grateful for any criticism. Especially, I am interested in if it is clear for average reader. For sure, this article is not error-free, precise or something. I will be grateful for any errata. The code can be taken from here. Of course the project was created with learning puproses in mind so think twice before using it in a real project. Test all first. For example, think about a problem where two mixins for a single aggregator exist with same public method name.

If you are interested in mixins, you can also walk Google.

Tags: , ,

2 Responses to “Practising OOP in PHP5: emulating mixins (Ruby-like)”

  1. thors_hammer123 says:

    Check out ClassMixer: https://github.com/wellspringworldwide/PHP-ClassMixer
    It gives you mixins and aspect-oriented programming in your PHP code.

  2. FractalizeR says:

    Thanks, I will look! 🙂

Leave a Reply

You must be logged in to post a comment. Login now.