开发者学堂课程【PHP 进阶教程-由浅入深掌握面向对象开发-第二阶段:PHP 属性重载】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/712/detail/12712
PHP 属性重载
内容介绍:
一、目标
二、概念
三、示例
四、小结
一、目标
了解 PHP 属性重载的意义,掌握 PHP 重载实现代码的容错性
二、概念
PHP 属性重载:当对象访问不存在的或者权限不够的属性的时候,自动触发的魔术方法让代码不出错(容错处理)
1.属性重载魔术方法
(1)_get (属性名):访问不存在或者权限不够的属性时触发
(2)_set (属性名,属性值):设置不存在或者权限不够的属性时触发(3)_isset (属性名):判定不存在或者权限不够的属性时触发
(4)_unset (属性名):删除不存在或者权限不够的属性时触发
三、示例
1.魔术方法
指系统为类中预先设计好的,只需要开发者实现的方法,魔术方法以双下划线--开始。对象在某些特定情况下会自动调用的方法。构造方法、析构方法和克隆方法就是魔术方法。
class Man{
public function _construct(){}
}
new Man() ;
#实例化后对象自动调用(触发时机:对象被实例化)
2.实现属性重载
如果想在外部读取私有属性而不报错,需要给一个名字来帮取
class Man{
#属性:私有
private $age = 10;
#读取重载
public function __get($key){
echo $key ,__METHOD__,'
';
}
#写重载
public function _set($key , $value){
echo $key . ' : ' . $value . '
';
}
#查是否存在重载
public function _isset($key){
echo $key,_METHOD__, '
'
}
#删除属性重载
public function _unset($key){
echo $key ,_METHOD__, '
';
}
}
#实例化
$m = new Man();
$m->age;
#类外访问私有属性:原本不允许
$m->age = 100;
#设置
isset($m->age) ;
#判定
unset($m->age);
#删除
class Man{
#属性:私有
private $age = 10;(年龄私有不允许访问日,如果想要访问需要实例化)
}
$m = new Man();
$m->age;(直接调用访问,不输出)
此时不允许访问
此时可以增加一个读取重载来解决
#读取重载
public function __get($key){
echo $key ,__METHOD__,'
';
}
如果需要访问不存在的: $m->name
也显示不存在
此时添加一项 name
再点击刷新,只剩一个 get。因为 age 没有权限,而 name 存在而且能访问,所以不会走读取重载。
如果读取重载后想要写入数据 $m->age++;
get 就不管用了
此时添加写重载。
#写重载
public function _set($key , $value){
echo $key . ' : ' . $value . '
';
}
#写入数据
Sm->age十十;
$m->age=12;
系统不理解为设置,而是读取,因此会走__get()
再运行
此时如果对 name 设置则根本不会走,因为它本身允许被设置。如果再往里放入其他东西也不影响,不会增加对应的属性,所以对象想要增加属性,不能直接添加属性,比如增加$m->gender = 'Male';
此时再打一个对象 var_dump($m);
点击刷新,发现并没有添加这个属性。
这是因为写重载并没有真正地增加对象的属性。如果要判定重载是否存在
#判定属性
var dump(isset($m->age));
点击刷新显示 false
这是因为我们没有设定它是不允许访问的,此时可以进行检查性的重载。
#查是否存在重载
public function __isset($key){
echo $key,__METHOD__,'
';
}
}
但是此时还是无法访问,因为在 isset 里并没有告知是否存在。
再来删除一个属性:
unset($m->age);
显示为不能访问私有。此时添加一个删除属性重载
#删除属性重载
public function _unset($key){
echo $key ,_METHOD__, '
';
}
}
此时系统没有报错,目的已经完成。
3.属性重载的目的
一方面为了不让程序运行出错,另一方面可以在类内部由我们自己控制内容的访问。比如在 get 的时候有些东西允许访问有些东西不允许访问,所以可以定义一个列表,如果访问的是 age,内部帮你访问,否则 false。
class Man{
private $age = 10;
#读取重载
public function __get($key){
# echo $key , '
';
#定义一个允许访问列表:假设有很多私有属性
$allow = array( 'age ');
#判定是否在列表内:在就允许访问,不在就返回 NULL 或者 false
if(in_array($key , $a11ow)){
return $this->$key;
#可变属性: $key 是外部访问的目标,最终为 $this->age
}
#不允许访问
return false;
}
可变属性相当于可变变量,本来 $this 的访问属性名不需要带$符号,但这时 $key 是变量,是最终取出来的结果。
再访问就会调用方法并且输出10,说明它走了这个方法,因为不允许访问,但是内部提供了结果可以使用。
如果 var_dump 一个不存在的 var_dump($m->gender);
此时显示 false
此时除了不让系统报错,而且内部限定了哪些东西可以分为哪些东西不能访问。
# 写重载(该方法没有返回值)
public function __set($key , $value){
# echo $key . ' : ' . Svalue . '
';
#与 _get 理论类似:允许的设置,不允许的不设置(什么都不做即可)
如果想让某些东西可写,需要增加一个列表,判断如果存在,就在里面帮它设置
}
#判定重载
public function __isset(Skey){
#给出内部判定结果
return isset($this->$key);
}
在判定时加一个内部判定,再进行判定时
False 变成了 true,说明属性是存在的。如果不加,系统还是 false。因为不能读取的话在外面判定不了。所以有了这个重载之后就可以在内部判定是否真有。
删除的另一种方法:
#对象重载
public function toString(){
#返回一个指定字符串(一般是当类有属性保存某些信息时,输出某个属性) return __METHOD__;
}
}
当对象被当作普通数据时会自动触发。对象是一种复合类型的数据,不能直接输出,所以会报错。
如果添加对象重载,刷新
public function __unset ( $key){
echo $key,__METHOD__,'
';
}
#对象重载
public function __toString(){
#返回一个指定字符串(一般是当类有属性保存某些信息时,输出某个属性) return __METHOD__;
}
}
$m = new Man();
# var__dump($m->age);
# var__dump($m->gender);
#$m->name;
#写入数据
# $m->age++; #系统不理解为设置,而是读取:因此会走 _get()
/*$m->age = 12;
$m->gender = ' Male ';
var_dump( $m);*/
#判定属性
#var_dump(isset($m->age));
#删除属性
#unset($m->age);
#直接输出对象
echo $m;
就可以在里面写好哪些东西是可以输出的,比如说对象可以把属性某一个给组装起来,或者组装成一条指令、代码或字符块,这样 echo 系统就不会出错。自从做了属性之后,凡是和属性操作甚至对象的直接输出相关的所有错误都不存在,都不会报错,但是外面是否能拿到正确的结果取决于内部的实现,做好了外部就可以用,没做好外部依然看不到正确结果,但是不会报错。
四、小结
1、PHP 中属性重载是为了保证数据的安全性,不让代码出现一些操作性错误。
2、PHP 重载机制 _get() 和 _set() 也可以理解为统一为属性设置访问和设置方法(便捷)。一般在 Java 里增加一个属性之后系统会额外增加两个方法,分别为 get 方法和 set 方法,但不是每一个属性都需要增加,针对属性需要传一个名字进去对应的类型限定。
3、属性重载也能有效保证对象结构,不会让用户无限增加对象属性。有了 _set() 之后就不能再添加对象,即使加了属性系统也不认。这样对象的属性就不会无限增加,因为增加越多对象就越慢,数据越大,本身内存的访问效率就比较低。
4、一般情况下系统内部使用的类如果不是特殊情况不需要使用到属性重载,如果类是对外提供操作的那么应该增加相应的重载机制以防出错。