PHP系统中给我们提供了很多预定义的方法,这些方法大部分都需要在类中声明,只有需要时才添加到类中。它们的作用、方法名称、使用的参数列表和返回值都是在PHP中预定好的,并且都是以两个下划线开始的方法名称。如果需要使用这些方法,方法体中的内容需要用户自己按需求编写。每一个预定义的方法都有它特定的作用,使用时不需要用户直接调用,而是在特定的情况下自动被调用。这一节中使用到的__set()、__get()、__isset()、和unset()四个方法,以及前面介绍过的构造方法“__construct()”和析构方法“_destruct”都是这样的方法,通常称为魔术方法。
一般来说,把类中的成员属性都定义为private的,这更符合现实的逻辑,能够更好地对类中成员起到保护作用。但是,对成员属性的读取和赋值操作是非常频繁的,而如果在类中为每个私有属性都定义可以在对象外部获取和赋值的公有方法,又是非常繁琐的。因此在PHP5.1.0以后的版本中,预定义了两个方法“__set()”、“__get()”,用来完成对所有私有属性都能获取和赋值的操作,以及用来检查私有属性是否存在的方法“__isset()”和用来删除对象私有属性的方法“__unset()”。
①魔术方法__set():
为了在程序运行过程中可以按要求改变一些私有属性的值,我们在类中给用户提供了公有的类似“setXxx()”方法这样的访问接口。这样做和直接为没有被封装的成员属性赋值相比,好处在可以控制将非法值赋给成员属性。因为经过公有方法间接为私有属性赋值时,可以在方法中做一些条件限制。但如果对象中的成员属性声明的比较多,而且还需要频繁操作,那么在类中声明很多个为私有属性重新赋值的访问接口则会加大工作量,而且还不容易控制。而是用魔术方法“__set()”则可以解决这个问题,控制在对象外部只能为私有的成员属性赋值,不能获取私有属性的值。需要在声明类时自己将它加到类中才可以使用,在类中声明的格式如下:
void __set(string name , mixed value) // 是以两个下划线开始的方法名,方法体的内容需要自己定义
该方法的作用是在程序运行过程中为私有属性设置值,它不需要有任何返回值。但它需要两个参数,第一个参数需要传入在为私有属性设置值时的属性名,第二个参数则需要传入为属性设置的值。而且这个方法不需要我们主动调用,可以在方法前面也加上private关键词修饰,防止用户直接去调用它。这个方法是在用户值为私有属性设置时自动调用的。在类中使用“__set()”方法的代码如下所示:
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
|
<?php class Person{
//下面是声明人员属性,全部使用了private关键字封装
private $name ;
private $sex ;
private $age ;
function __construct( $name = " " , $sex = "男" , $age =1) {
$this ->name = $name ;
$this ->sex = $sex ;
$this ->age = $age ;
}
//声明魔术方法需要两个参数,直接为私有属性赋值时自动调用,并可以屏蔽一些非法赋值
private function __set( $propertyName , $propertyValue ) {
//如果第一个参数是属性名sex则条件成立
if ( $propertyName == "sex" ){
//第二个参数只能是男或女
if (!( $propertyValue == "男" || $propertyValue == "女" ))
//如果是非法参数返回空,则结束方法可执行
return ;
}
//如果第一个参数是属性名age则条件成立
if ( $propertyName == "age" ){
//第二个参数只能在0到150之间的整数
if ( $propertyValue ) > 150 || $propertyValue <0)
//如果是非法参数返回空,则结束方法执行
return ;
}
//根据参数决定为哪个属性被赋值,传入不同的成员姓名,赋上传入的相应的值
$this -> $propertyName = $propertyValue ;
}
//下面是声明人类的成员方法,设置为公有的可以在任何地方访问
public function say(){
echo "我的名字:" . $this ->name. ",性别:" . $this ->sex. ",年龄:" . $this ->age. ".<br>" ;
}
} $person1 = new person( "张三" , "男" ,20);
//以下三行自动调用了__set()函数,将属性名分别传给第一个参数,将属性值传给第二个参数 $person1 ->name = "李四" ; //自动调用了__set()方法为私有属性name赋值成功
$person1 ->sex = "女" ; //自动调用了__set()方法为私有属性sex赋值成功
$person1 ->age = 80; //自动调用了__set()方法为私有属性name赋值成功
$person1 ->sex = "保密" ; //保密是一个非法值,这条语句给私有属性sex赋值失败
$person1 ->age = 800; //800是一个非法值,这条语句给私有属性age赋值失败
$person1 ->say(); //调用$person1对象中的say()方法,查看一下所有被重新设置的新值
?> |
该程序运行后输出的结果为:
我的名字叫:李四,性别:女,我的年龄是:80。
在上面的Person类中,将所有的成员属性设置为私有的,并将魔术方法__set()声明在这个类里面。在对象外面通过对象的引用就可以直接为私有的成员属性赋值了,看上去就像没有被封装一样。但在赋值过程中自动调用了__set()方法,并将直接赋值时使用的属性名传给了第一个参数,将值传给了第二个参数。通__set()方法简介的为私有属性设置心智。这样就可以在__set()方法中通过两个参数为不同的成员属性限制不同的条件,屏蔽掉为一些私有属性设置的非法值。例如在上例中没有对对象中的成员属性$name 进行限制,所以可以为它设置任意的值。但对对象中的成员属性$sex 限制了只能有“男”或“女”两个值,并且限制了在对象中成员属性$age设置值时,只能是0到150之间的证书。
②魔术方法__get():
如果在类中声明了__get()方法,则直接在对象的外部获取私有属性的值时,会自动调用此方法,返回私有属性的值。并且可以在__get()方法中根据不同的属性,设置一些条件来限制对私有属性的非法取值操作。和__set()一样,需要在声明类时自己将它添加到类中才可以使用,在类中声明的格式如下:
mixed__get(string name) //需要一个属性名作为参数,并返回处理后的属性值。
该方法的作用是在程序运行过程中,通过它可以在对象外部获取私有成员属性的值。它有一个必选的参数,需要传入在获取私有属性值时的属性名,并返回一个值,是在则个方法中处理后的允许对象外部使用的值。而且这个方法也不需要我们主动调用,也可以在方法前面加上private关键词修饰,防止用户直接去调用它。如果不在类中添加这个方法而直接获取私有属性的值,也会出现“不能访问某个私有属性”的错误。在类中使用“__get()”方法的代码如下所示:
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
|
<?php class Person {
private $name ;
private $sex ;
private $age ;
function __construct( $name = "" , $sex = "男" , $age =1){
$this ->name = $name ;
$this ->sex = $sex ;
$this ->age = $age ;
}
//在类中添加__get()方法,在直接获取属性时自动调用一次,以属性名作为参数传入并处理
private function __get( $propertyName ){ //在方法前使用private修饰,防止对象外部调用
if ( $propertyName == "sex" ){ //如果参数传入的是"sex"则条件成立
return "保密" ; //不让别人获取到性别,以"保密"替代
} else if ( $propertyName == "age" ){ // 如果参数传入的是"age"则条件成立
if ( $this ->age > 30) //如果对象中的年龄大于30时条件成立
return $this ->age -10; //返回对象中的虚假的年龄比真实年龄小10岁
else //如果对象中的年龄不大于30时,则执行下面代码
return $this -> $propertyName ; //让访问都可以获取到对象中的真实年龄
} else { //如果参数传入的是其他属性名则条件成立
return $this -> $propertyName //对其他属性都没有限制,可以直接返回属性的值
}
}
}
$presen1 = new Person( "张三" , "男" ,40);
echo "姓名:" . $persen1 -> name. "<br>" ; //直接访问私有属性name,自动调动了__get()方法可以间接获取
echo "性别:" . $person1 -> sex. "<br>" ; //自动调用了__get()方法,但在方法中返回真实属性值
echo "年龄:" . $age -> age. "<br>" ; //自动调用了__get()方法,根据对象本身的情况会返回不同的值
?> |
该程序运行后输出的结果:
姓名:张三 性别:保密 年龄:30
在上面的程序中声明了一个Person类,并将所有的成员属性使用 private修饰,还在类中添加了__get()方法。在通过该类的对象直接获取私有属性的值时,会自动调用__get()方法间接的获取到值。在上例中的__get()方法,没有对 $name属性进行限制,所以直接访问就可以获取到对象中真是的$name 属性值。单并不想让对象外部获取到$sex 属性值,所以当访问它时在_get()方法中返回“保密”.而且也对$age 属性做了限制,如果对象中年龄大于30,则隐瞒10岁,如果这个人在30岁以下则返回真实年龄。
③魔术方法__isset()和_unset()
学习“__isset()”方法之前我们先来了解以下“isset()”函数的应用,它是用来测定变量是否存在的函数。传入一个变量作为参数,如果传入的变量存在则返回 true,否则传回 false.那么是否可以用“isset()”函数测定对象里面的成员属性是否存在呢?如果对象中的成员属性是公有的,我们就可以直接使用这个函数来测定。但如果是私有的成员属性,这个函数就不起作用了,原因就是私有的被封装了,在对象外部不可见。但如果在对象中存在“__isset()”方法,当在类外部使用“isset()”函数来测定对象里面的私有属性时,就会自动调用类里面的“isset()”方法帮助哦我们完成测定的操作。“isset()”方法在类中声明的格式如下所示:
bool __isset(string name) //传入对象中的成员属性名作为参数,返回测定的结果
如果类中添加此方法,在对象的外部使用“isset()”方法测定对象中的成员时,就胡izidong调用对象中的“__isset()”方法,间接地帮助我们完成对对象中私有成员属性的测定。为了防止用户主动调用这个方法,也需要使用private关键字修饰将它封装在对象中。
学习“__unset()”方法之前,我们也要先来了解以下“unset()”函数。“unset”函数的租用是删除指定的变量,参数为要删除的变量名称。也可以使用这个函数在对象外部删除对象中的成员属性,单这个对象中的成员属性必须是公有的才可以直接删除。如果对象中的成员属性被封装,就需要在类中添加“__unset()”方法,才可以在对象的外部使用“unset()”函数直接删除对象中的私有成员属性时,自动调用对象中的“__unset()”方法帮助我们间接地将私有成员属性删除。也可以在“__unset()”方法中限制一些条件,组织删除一些重要的属性。__unset()方法在类中声明格式如下所示:
void __unset(string name) //传入对象中的成员属性作为参数,可以将私有成员属性删除如果没有在类中加入此方法,就不能删除对象中任何的私有成员属性。为了防止用户主动调用这个方法,也需要使用private关键字修饰将它封装在对象中。
在下面的示例中,声明一个Person类,并将所有的成员属性设置成private的。在类中添加自定义的“__isset()”和“__unset()”两个方法,在对象外部使用“isset()”和“unset()”函数时,会自动调用这两个方法。代码如下所示:
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
|
<?php class Person {
private $name ;
private $sex ;
private $age ;
function __construct( $name = "" , $sex = "男" , $age =1){
$this ->name = $name ;
$this ->sex = $sex ;
$this ->age = $age ;
}
//当在对象外面使用isset()测定私有成员属性时,自动调用,并在内部测定和传给外部的isset()结果
private function __isset( $propertyName ){ //在方法前使用private修饰,防止对象外部调用
if ( $propertyName == "name" ){ //如果参数传入的是"name"则条件成立
return false; //返回假,不允许在对象外部测定这个属性
return isset( $this -> $propertyName ); //其他的属性可以被测定,并返回测定的结果
}
//当在对象外面使用unset()方法删除私有属性时,自动被调用,并在内部把私用的成员属性删除
private function __unset( $propertyName ){ //需要一个参数,是要删除的私有属性名称
if ( $propertyName == "name" ) //如果参数中传入的属性名等于“name”时条件成立
return ; //退出方法,不允许删除对象中的name属性
unset( $this -> $propertyName ); //在对象的内容删除在对象外指定的私有属性
}
public function say(){
echo "我的名字:" . $this ->name. ",性别:" . $this ->sex. ",年龄:" . $this ->age. ",<br>" ;
}
}
$presen1 = new Person( "张三" , "男" ,40); //创建一个对象$person1,将成员属性分别附上初值
var_dump(isset( $person1 -> name)); //输出bool(false),不允许测定name属性
var_dump(isset( $person1 -> sex)); //输出bool(true),存在sex私有属性
var_dump(isset( $person1 -> age)); //输出bool(true),对象中存在age私有属性
var_dump(isset( $person1 -> id)); //输出bool(false),测定对象中不存在id属性
unset( $person1 ->name); //删除私有属性name,但在__unset()中不允许删除
unset( $person1 ->sex); //删除对象中的私有属性sex,删除成功
unset( $person1 ->age); //删除对象中的私有属性age,删除成功
$person1 ->say(); //对象中的sex和age属性被删除,输出: 我的名字叫张三,性别: , 我的年龄是:
?> |
在上面的程序中定义了一个Person类,并将三个成员属性声明为private,又在类中添加了__isset()和__unset()两个方法。通过Person类创建了一个对象person1,当使用isset()函数测定对象person1中是否存在某个私有成员属性时,就会自动调用它本身对象中的isset()方法,并将指定的属性名称传入进来。当__isset()方法中除了将成员属性name隐藏起来不允许外部检查之外,其他的私有成员属性都可以被测定。当使用unset()函数删除对象person1中的某个私有成员属性时,就会自动调用本身对象中的__unset()方法来完成。在__unset()方法中设置出了私有成员属性name不能被删除外,其他的私有成员属性都可以在对象外部使用unset()函数删除。