From: Andrey Kutejko Date: Thu, 12 Sep 2013 11:28:19 +0000 (+0300) Subject: mixins X-Git-Tag: 0.1~13 X-Git-Url: https://git.andy128k.dev/?a=commitdiff_plain;h=f43d941b3a57bcebb9e78ba32e2bf7d42692f7ee;p=missing-tools.git mixins --- diff --git a/src/mixins.php b/src/mixins.php new file mode 100644 index 0000000..78897b8 --- /dev/null +++ b/src/mixins.php @@ -0,0 +1,91 @@ +methods[strtolower($method)][] = $mixin; + } + } + + private $current_call_method = null; + private $current_call_mixins = null; + + public function __call($method, $args = array()) + { + $m = strtolower($method); + if (!isset($this->methods[$m])) + throw new Exception('Call to undefined method ' . get_class($this) . "::$method()"); + + $this->current_call_method = $method; + $this->current_call_mixins = $this->methods[$m]; + $result = call_user_func_array(array($this, 'call_next_method'), $args); + $this->current_call_method = null; + $this->current_call_mixins = null; + return $result; + } + + public function call_next_method() + { + if ($this->current_call_mixins === null) + throw new Exception('call_next_method is invoked outside of mixin method.'); + + $mixin = array_pop($this->current_call_mixins); + if ($mixin === null) + throw new Exception('No next method for ' . get_class($this) . "::$method()"); + + return call_user_func_array(array($mixin, $this->current_call_method), func_get_args()); + } +} + +abstract class Mixable +{ + protected abstract function __mixins(); + + protected $mixins; + + public function __construct() + { + $this->mixins = new Mixins($this, $this->__mixins()); + } + + public function __call($method, $args = array()) + { + return call_user_func_array(array($this->mixins, $method), $args); + } + + public function call_next_method() + { + return call_user_func_array(array($this->mixins, 'call_next_method'), func_get_args()); + } + + public function __clone() + { + } +} + +class Mixin +{ + protected $self; + + public function __construct($self) + { + $this->self = $self; + } + + protected function call_next_method() + { + return $this->self->call_next_method(func_get_args()); + } +} + diff --git a/t/MixinsTest.php b/t/MixinsTest.php new file mode 100644 index 0000000..d634305 --- /dev/null +++ b/t/MixinsTest.php @@ -0,0 +1,62 @@ +self->b = $newValue; + } +} + +class WrapMixin extends Mixin +{ + public function getSmthUseful() + { + return '(' . $this->call_next_method() . ')'; + } + + public function doSmthElse($str) + { + return strrev($str); + } +} + +class Mixture extends Mixable +{ + protected function __mixins() { return array('SomeMixin', 'WrapMixin'); } + + public $b = 'b'; + + public function doSmthElse($str) + { + return strtoupper($this->mixins->doSmthElse($str)); + } +} + +class MixinsTest extends PHPUnit_Framework_TestCase +{ + public function testSetter() + { + $mixture = new Mixture; + $mixture->setB('B'); + $this->assertEquals('B', $mixture->b); + } + + public function testWrapping() + { + $mixture = new Mixture; + $this->assertEquals('(a)', $mixture->getSmthUseful()); + } + + public function testOverriding() + { + $mixture = new Mixture; + $this->assertEquals('LARCH', $mixture->doSmthElse('hCraL')); + } +} +