PHP中 对象自动调用的方法:__set()、__get()、__tostring()

总结:

 (1)__get($property_name):获取私有属性$name值时,此对象会自动调用该方法,将属性name值传给参数$property_name,通过这个方法的内部

                                             执行,返回我们传入 的私有属性的值。

 (2)__set($property_name, $value):直接给私有属性赋值时,此对象会自动调用该方法,把属性比如name传给$property_name, 把要赋的值

                                                         “zhangsan”传给$value,通过这个方法的执行,达到赋值的目的。

 (3)__tostring()                : 当直接输出句柄(可以理解为一个实例)时,会自动执行__tostring()方法。

1.__set()与__get().

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

“__get()”和“__set()”来获取和赋值其属性,以及检查属性的“__isset()”和删除属性的方法 “__unset()”。

       我们为每个属性做了设置和获取的方法,在PHP5中给我们提供了专门为属性设置值和获 取值的方法,“__set()”和“__get()”这两个方法,这两个方法不是默认存在的,

而是我们手工添加到类里面去的,像构造方法 (__construct())一样, 类里面添加了才会存在,可以按下面的方式来添加这两个方法,当然也可以按个人的风格来添加:

//__get()方法用来获取私有属性
public function __get($property_name)
{
 
 if(isset($this->$property_name)){
 return($this->$property_name);
 }else{
 return(NULL);
 }
}

//__set()方法用来设置私有属性
public function __set($property_name, $value){
$this->$property_name = $value;
}

__get()方法:这个方法用来获取私有成员属性值的,有一个参数,参数传入 你要获取的成员属性的名称,返回获取的属性值,这个方法不用我们手工的去调用,因为我们也可以把这个方法做成私有的方法,是在直接获取私有属性的时候对象 自动调用的。因为私有属性已经被封装上了,是不能直接获取值的(比如:“echo $p1->name”这样直接获取是错误的),但是如果你在类里面加上了这个方法,在使用“echo $p1->name”这样的语句直接获取值的时候就会自动调用__get($property_name)方法,将属性name传给参 数$property_name,通过这个方法的内部执行,返回我们传入的私有属性的值。如果成员属性不封装成私有的,对象本身就不会去自动调用这个方 法。

__set()方法:这个方法用来为私有成员属性设置值的,有两个参数,第一个参数为你要为设置值的属性名,第二个参数是要给属性设置的值, 没有返回值。这个方法同样不用我们手工去调用,它也可以做成私有的,是在直接设置私有属性值的时候自动调用的,同样属性私有的已经被封装上

了, 如果没有__set()这个方法,是不允许的,比如:$this->name=‘zhangsan’, 这样会出错,但是如果你在类里面加上了__set($property_name, $value)这个方法,在直接给私有属性赋值的时候,就会自动调用它,把属性比如name传给$property_name, 把要赋的值“zhangsan”传给$value,通过这个方法的执行,达到赋值的目的。如果成员属性不封装成私有的,对象本身就不会去自动调用这个方 法。为了不传入非法的值,还可以在这个方法给做一下判断。代码如下:

<?php
class Person {
 
 //下面是人的成员属性, 都是封装的私有成员
 private $name; //人的名子
 private $sex; //人的性别
 private $age; //人的年龄
 
 //__get()方法用来获取私有属性
 public function __get($property_name) {
 echo "在直接获取私有属性值的时候,自动调用了这个__get()方法<br>";
 if (isset ( $this->$property_name )) {
 return ($this->$property_name);
 } else {
 return (NULL);
 }
 }
 
 //__set()方法用来设置私有属性
 public function __set($property_name, $value) {
 echo "在直接设置私有属性值的时候,自动调用了这个__set()方法为私有属性赋值<br>";
 $this->$property_name = $value;
 }
}

$per=new person();
$per->name="shirayner";  //此时$per自动调用__set($property_name, $value)方法
echo $per->name;        //此时$per自动调用__get($property_name)方法

?>

2.__tostring() 
   
    TOstring(在这里故意这么写,是要说明PHP中方法不区分大小写,但实际开发中还需要注意规范)。当进行测试时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属
性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。如果没有这个方法,那么echo一个对
象将报错,例如“Catchable fatal error:Object of class Account could not be converted tostring”语法错误,实际上这是一个类型匹配失败错误。不过仍然可以用print_r()
和var_dump()函数输出一个对象。当然,toString是可以定制的,所提供的信息和样式更丰富。
<?php
class Account{
public $user=1;
private $pwd=2;
// 自定义的格式化输出方法
public function toString(){
return "当前对象的用户名是{$this->user},密码是{$this->pwd}";
}
}
$a=new Account();
echo $a;
echo PHP_EOL;
print_r($a);

      运行这段代码发现,使用toString方法后,输出的结果是可定制的,更易于理解。实际上,PHP的toString魔术方法的设计原型来源于 Java。Java中也有这么一个方法,而且在Java中,这个方法被大量使用,对于调试程序比较方便。实际上,toString方法也是一种序列化,我 们知道PHP自带serialize/unserialize也是进行序列化的,但是这组函数序列化时会产生一些无用信息,如属性字符串长度,造成存储空 间的无谓浪费。因此,可以实现自己的序列化和反序列化方法,或者json_encode/json_decode也是一个不错的选择。

为什么直接echo一个对象就会报语法错误,而如果这个对象实现toString方法后就可以直接输出呢?原因很简单,echo本来可以打印一个对 象,而且也实现了这个接口,但是PHP对其做了个限制,只有实现toString后才允许使用。从下面的PHP源代码里可以得到验证:

ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMP|VAR|CV, ANY)
{
zend_op *opline = EX(opline);
zend_free_op free_op1;
zval z_copy;
zval *z = GET_OP1_ZVAL_PTR(BP_VAR_R);
// 此处的代码预留了把对象转换为字符串的接口
if (OP1_TYPE != IS_CONST &&
Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_method != NULL &&
zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) {
zend_print_variable(&z_copy);
zval_dtor(&z_copy);
} else {
zend_print_variable(z);
}
FREE_OP1();
ZEND_VM_NEXT_OPCODE();
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mukekeheart的iOS之旅

OC学习10——内存管理

1、对于面向对象的语言,程序需要不断地创建对象。这些对象都是保存在堆内存中,而我们的指针变量中保存的是这些对象在堆内存中的地址,当该对象使用结束之后,指针变量指...

2275
来自专栏有趣的django

python面试题(持续更新)

第1~10题 1、一行代码实现1--100之和 >>> sum(range(1,101)) 5050 >>> 2、如何在一个函数内部修改全局变量 a= 3 ...

41811
来自专栏前端儿

ES6笔记(5)-- Generator生成器函数

Generator的声明方式类似一般的函数声明,只是多了个*号,并且一般可以在函数内看到yield关键字

861
来自专栏python3

python字典

所不同的是列表的索引只是从0开始的有序整数,不可重复;而字典的索引实际上在字典里应该叫键,虽然字典中的键和列表中的索引一样是不可重复的,但键是无序的,也就是说字...

962
来自专栏java一日一条

(转)Java中的System类

System类代表系统,系统级的很多属性和控制方法都放置在该类的内部。该类位于java.lang包。

912
来自专栏陈树义

从 HelloWorld 看 Java 字节码文件结构

很多时候,我们都是从代码层面去学习如何编程,却很少去看看一个个 Java 代码背后到底是什么。今天就让我们从一个最简单的 Hello World 开始看一看 J...

4157
来自专栏玄魂工作室

《改善C程序代码的125个建议》-防止整数类型产生回绕与溢出

以下内容摘抄自《改善C程序代码的125个建议》: 建议2:防止整数类型产生回绕与溢出 到C99为止,C语言为我们提供了12个相关的数据类型关键字来表达各种数据...

2927
来自专栏ShaoYL

深刻理解----修饰变量----关键字

34911
来自专栏极客编程

ECMAScript 6教程 (三) Class和Module(类和模块)

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文连接,博客地址为 http://www.cnblogs.co...

923
来自专栏前端儿

理解运用JS的闭包、高阶函数、柯里化

JS的闭包,是一个谈论得比较多的话题了,不过细细想来,有些人还是理不清闭包的概念定义以及相关的特性。

2143

扫码关注云+社区