在PHP中有一些很独特的方法,只要定义了它们,就会在某些事件发生的时候很神奇的自动调用,也因此它们被称为魔术方法,特点是以__开头,本篇kano个人博客技术文章就给大家介绍一下这些神奇的魔术方法。(以__开头是魔术方法的特点,所以kano建议大家定义自己的方法时不要以__为前缀)。
※ 对魔术方法较为准确的一个描述就是“事件处理器”,学过js和mysql触发器的同学应该较为容易理解。
魔术方法的方法名和参数是固定的。
1. __set和__get
通常在定义类的时候,会有一些不可直接访问的的私有属性。有时候我们需要有限制的访问它们,会给它们设置属性访问器(即get或set,学过Java的同学应该很熟悉)。在php中,属性访问器由__set和__get统一设置(而不是像Java一样为每个属性单独设置)。在给不可访问的属性赋值时,__set会被调用;在获取不可访问的属性时,__get会被调用。
注意一点:不可访问可能是private也可能是protected,甚至可能是不存在的属性,只要是在当前位置访问不到的属性都会调用起它们。
来看一下两个方法的定义:{
void __set(string name, mixed value)
public __get( string $name) : mixed
}
__set接受两个参数:name是属性名,value为属性值;__get只有一个参数name,为属性名。两个方法都不能是静态的切必须为public。
来看一个例子:
class Person { private $name; private $age; public $job; public function __set($name, $value) { echo "__set被调用,给{$name}赋值<br>"; switch ($name) { case 'name': $this->name = $value; break; case 'nianling': if ($value > 100) { $this->age = 100; } else if ($value < 0) { $this->age = 0; } else { $this->age = $value; } break; case 'job': $this->job = $value; break; } } public function __get($name) { if ($name == 'mess') { return ['name' => $this->name, 'age' => $this->age, 'job' => $this->job]; } else { return "{$name}为不合法的属性"; } } function say() { echo "名字为:{$this->name},年龄为:{$this->age},职业为:{$this->job}<br>"; } } $person1 = new Person(); $person1->name = '王一'; $person1->nianling = 150; $person1->job = 'coder'; $person1->say(); print_r($person1->mess); print_r($person1->aaa);
第一行实例化了一个对象:
首先给name赋值,可以看到__set被调用,
然后给不存在的属性nianling赋值,发现__set也是会被调用,
给public属性job赋值,发现__set没有被调用,
调用say方法,可以看到赋值的结果:name成功赋值“王一”,给nianling的赋值经过了数据的处理,赋给了age,可见属性job未经过__set方法,普通的赋上了值,
获取不存在的属性mess,通过__get方法返回了一个数组,
获取不存在的属性aaa,通过__get方法输出其为不合法的属性。
2. __clone
克隆对象时会被调用,比如你想克隆某个对象,但是相对其中的一些值进行初始化:
class Person { public $name; public $age; static $count = 0; public function __construct($name, $age) { $this->name = $name; $this->age = $age; self::$count++; } public function __clone() { $this->age = 0; self::$count++; } function say(){ echo "名字为:{$this->name},年龄为:{$this->age},总人数为:" . self::$count . "<br>"; } } $p1 = new Person('王一', 15); $p1->say(); $p2 = clone $p1; $p2->say();
通过克隆p1得到p2,可以看到p2的年龄重置为了0;默认总人数只在实例化对象时增加,这里kano个人技术博客把人数自增也写到克隆里,保证了总人数的正确性。
__clone方法没有参数,因为克隆的操作通过关键字clone实现,无法接受参数。
3. __toString
对象被当成一个字符串时自动调用:比如有时候我们想直接打印一下对象的信息,但是如果你用 echo 直接输出一个对象,php会报一个"Object of class XXX could not be converted to string"(XXX类的对象无法转换成字符串)的错误,这时我们就可以定义 __toString 魔术方法并返回一个字符串,这样在直接打印对象的时候则会自动调用 __toString 方法了
Kano还用上面的例子,只贴_toString方法的代码:
public function __toString() { return "名字为:{$this->name},年龄为:{$this->age},附加信息:哈哈哈哈哈哈哈哈哈哈"; } $p1 = new Person('王一', 15); echo $p1;
4. __call
调用一个不可访问的方法时会自动调用,这里的“不可访问”和上面的不可访问属性一样,可能是方法的可见性,也可能是方法根本就不存在。
定义: public __call( string $name, array $arguments) : mixed
__call方法接受两个参数,第一个是方法的名字,第二个是方法的参数列表。
没有用到__call方法的时候调用没有定义的方法,一定会出现系统报错,所以__call方法可以用作这种情况下的错误处理;也可以用作如一个方法有几个别名,直接用__call处理就不必多写几个方法然后在其中调用了。
当然方法如果是可见的话则不会调用__call方法。
例:
function __call($funName, $params){ echo "方法{$funName}不存在!<br /><br />"; echo '参数列表:'; print_r($params); } $p1 = new Person('王一', 15); $p1->fang('这是', '一个', '不存在', '的方法');
如果同学们用过ThinkPHP,可能会注意到一个现象,一般的方法按Ctrl点击会跳转到方法定义,但是有一些方法是无法跳转的,如查询语句中的聚合函数,翻开ThinkPHP源码,就可以看到聚合函数的操作实际上是由__call来完成的:
(可以看到除了聚合函数还有很多其他的方法)
5. __autoload
在尝试调用一个不存在的类的时候自动触发,接受一个参数,即不存在的类名,类名会自动传入到 __autoload 中(当本php文件中没有,并且include的php文件中也没有对应的类的时候才会触发)
这个魔术方法有一点比较特殊的地方——它并不是在类中定义的,而是一个全局函数(所以可能叫“魔术函数”更恰当一点)
例子1:
test1.php中的内容 <?php class Person{ function __construct(){ echo 'test1 Person'; } } function __autoload($className){ echo "类{$className}不存在,__autoload被调用<br>"; include 'test2.php'; } $person1 = new Person(); ?>
test2.php中的内容
<?php class Person{ function __construct(){ echo 'test2 Person'; } } ?>
输出 "test1 Person",因为在当前文件中能找到 Person
例子2:
test1.php中的内容
<?php function __autoload($className){ echo "类{$className}不存在,__autoload被调用<br>"; include 'test2.php'; } $person1 = new Person(); ?>
test2.php中的内容
<?php class Person{ function __construct(){ echo 'test2 Person'; } } ?>
输出 "test2 Person",__autoload被触发。
这个例子可以很好的看出__autoload的触发方式,不过这并不是__autoload的实际用途,__autoload一般用法为:将php文件规范命名,以达成调用某个类的时候能够自动引入对应文件的目的。
※ 这个函数在PHP7.2.0版本被标记为不鼓励使用的,考虑到这个版本如此之新,且是“不鼓励”而不是“废弃”,看来__autoload还能用很长时间。
6. ____construct和__destruct(构造方法和析构方法)
这两个方分别在对象创建和销毁的时候被调用,在“php对象简介”中已经详细介绍过它们,这里不再重复。
以上就是kano个人博客关于PHP魔术方法简介的教程,可以看到只要定义了魔术方法,它们就会在各种事件发生的时候调用,如果善用的话,可以省去不少的代码和思考负担。