官方手册上的评论里不推荐在生产环境使用重载:不利于多人协作,代码可维护性变差,容易导致不可预料的错误等。
重载分为属性重载和方法重载。
重载的作用是动态的创建类属性和方法,通过php内置的专用的魔术方法实现。
属性重载用到的魔术方法:
public __set(string $name, mixed $value):void
public __get(string $name):mixed
public __isset(string $name):bool
public __unset(string $name):void
方法重载用到的魔术方法:
public __call(string $name, array $arguments):mixed
public static __callStatic(string $name, array $arguments):mixed
属性发生重载的情形:
$this
访问未声明过的属性(此时在类中声明的魔术方法会被调用)重载对于private
或protected
属性不起作用,对于已经在类中声明过的public
属性也不起作用。
对于在类中声明过的private
或protected
属性,如果通过类的实例对象访问同名属性,相当于重新创建了一个属性。
通过代码验证:
// php 版本5.6.9 class Test { private $data = array(); public $name = "test"; private $age = 12; protected $height = 180; // 魔术方法 public function __set($key,$val) { echo "set {$key} to {$val}. \n"; $this->data[$key] = $val; } public function __get($key) { echo "Get {$key}. \n"; if(array_key_exists($key,$this->data)) { return $this->data[$key]; } return null; } public function __isset($key) { echo "Check {$key} is set. \n"; return isset($this->data[$key]); } public function __unset($key) { echo "Unset {$key}. \n"; unset($this->data[$key]); } function getAge() { return $this->age; } function getHeight() { return $this->height; } function getQ() { return $this->q; } function getName() { return $this->name; } } $t = new Test();
通过实例对象/$this
访问未声明的属性:
$t->getQ(); // Get q. $t->q; // Get q. $t->q = 123; // set q to 123. echo $t->getQ()."\n"; // Get q. 123
访问已声明过的public
属性:
$t->name; // 没有打印输出 魔术方法没有调用 $t->getName(); // 没有打印输出 魔术方法没有调用
访问private
和protected
属性:
echo $t->getAge()."\n"; // 12 魔术方法没有调用 echo $t->getHeight()."\n"; // 180 魔术方法没有调用 $t->age = 123; // set age to 123. $t->height = 456; // set height to 456. echo $t->getAge()."\n"; // 12 魔术方法没有调用 虽然通过$this访问,但取的值不是123 echo $t->getHeight()."\n"; // 180 魔术方法没有调用 虽然通过$this访问,但取的值不是456
在对象中调用一个不可访问方法(包括未在类中声明的方法,private
和protected
方法)时, __call(string $name, array $arguments)
会被调用。
如果通过实例对象调用了在类中声明过的同名的private
或protected
方法,那么相当于新建了一个同名的方法调用,跟原来的private
或protected
方法没有关系,原来的private
或protected
方法也不受影响。
在静态上下文中调用一个不可访问方法时,__callStatic(string $name, array $arguments)
会被调用。
这里的静态上下文有多种情形:
// php 版本5.6.9 class MethodTest { public function __call($name, $arguments) { // 注意: $name 的值区分大小写 echo "Calling object method '$name' " . implode(', ', $arguments). "\n"; } public static function __callStatic($name, $arguments) { // 注意: $name 的值区分大小写 echo "Calling static method '$name' " . implode(', ', $arguments). "\n"; } private function t() { echo "t function call. \n"; } protected function tt() { echo "tt function call. \n"; } static function ttt() { echo "ttt function call. \n"; } function aaa() { MethodTest::tee(); } static function bbb() { static::tee(); self::tee(); } } $obj = new MethodTest;
情形一:
$obj->runTest('in object context'); MethodTest::runTest('in static context'); // 直接通过类名加双冒号调
情形二:
MethodTest::aaa(); // 情形二本质上还是情形一
情形三:
$obj->bbb(); // 在静态方法中
$name
参数是要调用的方法名称。$arguments
参数是一个枚举数组,包含着要传递给方法 $name
的参数。