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');