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()函数删除。

 

>> 本文固定链接: http://php.ncong.com/php_course/oop/phpmoshufangfa.html

>> 转载请注明: 恩聪php 2014年07月24日 于 恩聪PHP学习教程 发表

详解PHP的__set()、__get()、__isset()、unset()四个方法的更多相关文章

  1. php __set() __get() __isset() __unset()四个方法的应用

    一般来说,总是把类的属性定义为private,这更符合现实的逻辑.但是,对属性的读取 和赋值操作是非常频繁的,因此在PHP5 中,预定义了两个函数“__get()”和“__set()”来获 取和赋值其 ...

  2. 详解OJ(Online Judge)中PHP代码的提交方法及要点【举例:ZOJ 1001 (A + B Problem)】

    详解OJ(Online Judge)中PHP代码的提交方法及要点 Introduction of How to submit PHP code to Online Judge Systems  Int ...

  3. 详解JavaScript数组过滤相同元素的5种方法

    详解JavaScript数组过滤相同元素的5种方法:https://www.jb51.net/article/114490.htm

  4. [C#详解] (1) 自动属性、初始化器、扩展方法

    文章来源:Slark.NET-博客园 http://www.cnblogs.com/slark/p/CSharp-focus-1.html 代码下载:点我下载 目录 前言 属性与自动属性 属性 自动属 ...

  5. 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码

    详解C#泛型(二)   一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...

  6. 第6章 传输层(详解TCP的三次握手与四次挥手)

    第6章 传输层 传输层简介 传输层为网络应用程序提供了一个接口,并且能够对网络传输提供了可选的错误检测.流量控制和验证功能.TCP/IP传输层包含很多有用的协议,能够提供数据在网络传输所需的必要寻址信 ...

  7. php之魔术方法 __set(),__get(),__isset(),__unset()

    __set()与__get() 当一个类里面,属性被设置为私有属性时,这个属性是不能在外部被访问的.那么当我们又想在外部访问时该怎么办呢,我们可以用方法来实现.举例如下: 1 class Test 2 ...

  8. Yii 框架里数据库操作详解-[增加、查询、更新、删除的方法 'AR模式']

    public function getMinLimit () {        $sql = "...";        $result = yii::app()->db-& ...

  9. java多线程详解(6)-线程间的通信wait及notify方法

    Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...

随机推荐

  1. c++封装性

    C++ code到运行程序 作为一个c++程序员这个应该是最应该知道的细节,简言之:编译----链接----可执行的程序.这里所说的细节主要是第一步的细节,编译器如何把c++代码编译成目标代码.概括的 ...

  2. owncloud乱码问题

    没有人回答那我就献丑了,废话有点多,请跳着看. 先说结论:最后我选择了 Seafile 我去年之前用box同步个人文件,前几个月的时候吧,发现box不能上传几百M的文件,我才知道之前为什么会丢文件了… ...

  3. ASCII、Unicode、GBK和UTF-8字符编码的区别联系[转]

    http://dengo.org/archives/901 这是我看过的最好的一篇讲述编码的文章 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到 ...

  4. Stack Overflow requires external JavaScript from another domain, which is blocked or failed to load.

    出现以上问题,只是说明stackoverflow前端库用到google的API来,所以stackoverflow躺枪.查阅后,是因为调用jquery的问题. 详情请看: 解决方案有好几种: 1.将ht ...

  5. COJ WZJ的数据结构(负十八)splay_tree的天堂

    WZJ的数据结构(负十八) 难度级别:E: 运行时间限制:100000ms: 运行空间限制:700KB: 代码长度限制:2000000B 试题描述 对于前一段样例: 输入 输入文件的第1行包含两个数N ...

  6. 2014-07-25 改进自定义菜单与使用SVN进行协同开发

    今天是在吾索实习的第13天.今天没有做过多的代码设计,只进行了一些代码的分析与进一步优化.其中,发现创建自定义菜单的关键代码书写可分为两部分: JSON格式的字符串在.net中的语法书写: strin ...

  7. 当程序遇到 throw后的处理

    不管你在何处throw出一个exception,后面的代码便不会执行,它会去匹配本层代码中是否有相应的catch语句来捕捉,如果有,则进入此catch块,执行其中的代码.这样这次异常算是处理完了,如果 ...

  8. zookeeper[4] 安装windows zookeeper,及问题处理

    安装步骤: 1.在如下路径下载zookeeper-3.4.7.tar.gz http://mirrors.cnnic.cn/apache/zookeeper/stable/ 2.解压zookeeper ...

  9. JAVA 并发实现六(Volatile的使用)

    Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量. 这两种机制的提出都是为了实现代码线程的安全性.其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低 ...

  10. RequireJS进阶(一)

    为了应对日益复杂,大规模的JavaScript开发.我们化整为零,化繁为简.将复杂的逻辑划分一个个小单元,各个击破.这时一个项目可能会有几十个甚至上百个JS文件,每个文件为一个模块单元.如果上线时都是 ...