PHP通过魔术方法实现多继承与重载

 PHP所提供的”重载”(overloading)是指动态地”创建”类属性和方法。我们是通过魔术方法(magic methods)来实现的。当调用当前环境下未定义或不可见的类属性或方法时,重载方法会被调用。本节后面将使用”不可访问属性”和”不可访问方法”来称呼这些未定义或不可见的类属性或方法。所有的重载方法都必须被声明为 public。

<?php
class Parent1 {
    public function printFunction ($args) {
        echo 'parent1->printfunction ' . $args . '<br>';
    }

    public function printFunction1 ($args) {
        echo 'parent1->printfunction1 ' . $args . '<br>';
    }
}

class Parent2 {
    public function printFunction2 ($args) {
        echo 'parent2->printfunction2 ' . $args . '<br>';
    }
}

class Child {
    protected $_parents = null;

    public function __construct($parents) {
        $this->_parents = $parents;
    }

    public function __call($method, $args) {
        foreach ($this->_parents as $p) {
            if (is_callable(array($p, $method))) {
                return call_user_func_array(array($p, $method), $args);
            }
        }
    }

    public function printFunction ($args) {
        echo 'child->printfunction ' . $args . '<br>';
    }
}

class A {
    protected $_parents = null;

    public function __construct($parents) {
        $this->_parents = $parents;
    }

    public function __call($method, $args) {
        foreach ($this->_parents as $p) {
            if (is_callable(array($p, $method))) {
                return call_user_func_array(array($p, $method), $args);
            }
        }
    }   
}

class B extends Child{
    public function __construct($parent){
        parent::__construct($parent);
    }
}
$obj = new Child(array(new Parent1(), new Parent2()));
$a = new A(array(new Child(array(new Parent1(), new Parent2()))));
$b = new B(array(new Parent1(), new Parent2()));

$obj->printFunction(123); // child->printfunction 123
$obj->printFunction1(456); // parent1->printfunction1 456
$obj->printFunction2(789); // arent2->printfunction2 789

$a->printFunction(123); // child->printfunction 123
$a->printFunction1(456); // parent1->printfunction1 456
$a->printFunction2(789); // arent2->printfunction2 789

$b->printFunction(123); // child->printfunction 123
$b->printFunction1(456); // parent1->printfunction1 456
$b->printFunction2(789); // arent2->printfunction2 789

当执行$obj->printFunction(123)时,由于在Child类中定义了此方法,所以直接调用Child中的printFunction方法。而执行$obj->printFunction1(456)和$obj->printFunction2(789)时,由于Child类中未定义printFunction1和printFunction2方法,就会自动调用__call方法,该方法的第一个参数是调用的不存在的方法的名称,是字符串类型,第二个参数是要传递的参数,是数组类型。在__call方法中查找引用的类的实例中是否存在该方法。类A和类B分别为实现不同的“继承”方法,他们的结果是一样的。用静态方式中调用一个不可访问方法时,__callStatic() 会被调用。

对于属性的重载也是同样的道理。

在给不可访问属性赋值时,__set() 会被调用。
读取不可访问属性的值时,__get() 会被调用。
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。
当对不可访问属性调用 unset() 时,__unset() 会被调用。

还请读者参照PHP文档进行具体实现,若要了解详细使用,可参考yii中yii\base\Object的property和yii\base\Component的behavior的具体实现。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注