PHP的Trait可以实现加载时(load time)的混入(mixin)。作为元编程的一部分,运行时(run time)的混入拥有更大的灵活性。下面利用PHP的魔术方法实现运行时的混入。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 <?php /** * 支持混入的类 */ class Component { // ... // 所有混入的实例 private $_behaviors = []; /** * 魔术方法 * @param string $name 方法名 * @param array $arguments 参数数组 * @return mixed * @throws MethodNotFoundException */ public function __call($name, $arguments) { foreach ($this->_behaviors as $behavior) { if (method_exists($behavior, $name)) { return call_user_func_array([$behavior, $name], $arguments); } } throw new MethodNotFoundException(get_class($this), $name); } /** * 魔术方法,从混入对象实例中取属性值 * @param string $attrName 属性名 * @return mixed * @throws AttrNotFoundException */ public function __get($attrName) { foreach ($this->_behaviors as $behavior) { if (property_exists($behavior, $attrName)) { return $behavior->$attrName; } } throw new AttributeNotFoundException(get_class($this), $attrName); } /** * 附加混入对象实例 * @param object $behavior 混入对象实例 * @param string $name 混入对象实例名称 * @return void */ public function attachBehavior($behavior, $name='') { if (empty($name)) $this->_behaviors[] = $behavior; else $this->_behaviors[$name] = $behavior; } /** * 卸载混入对象实例 * @param string $name 混入对象实例名称 * @return void */ public function detachBehavior($name) { unset($this->_behaviors[$name]); } // ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php /** * 混入类 */ class Behavior { // ... /** * 将本实例混入指定对象 * @param object $object 支持混入的实例 * @param string $name 目标对象存储本混入对象实例的键值 * @return void * @throws BehaviorNotAttachableException */ public function mixin($object, $name='') { if (method_exists($object, 'attachBehavior')) { return call_user_func_array([$object, 'attachBehavior'], [$this, $name]); } throw new BehaviorNotAttachableException(get_class($object)); } // ... } 使用示例: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php include_once 'component.php'; include_once 'behavior.php'; class TestBehavior extends Behavior { public function test($what) { echo "say $what"; } } $c = new Component(); $b = new TestBehavior(); $c->attachBehavior($b, 'test'); echo '<pre>'; var_dump($c); echo '</pre>'; $c->detachBehavior('test'); echo '<pre>'; var_dump($c); echo '</pre>'; $b->mixin($c, 'test'); echo '<pre>'; var_dump($c); echo '</pre>'; $c->detachBehavior('test'); $b->mixin($c); echo '<pre>'; var_dump($c); echo '</pre>'; $c->test('hello world');