看另外一个魔术方法TOstring(在这里故意这么写,是要说明PHP中方法不区分大小写,但实际开发中还需要注意规范)。
当进行测试时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是什么,如果类定义了toString方法,就能在测试时,echo打印对象体,对象就会自动调用它所属类定义的toString方法,格式化输出这个对象所包含的数据。如果没有这个方法,那么echo一个对象将报错,例如“Catchable fatal error:Object of class Account could not be converted to string”语法错误,实际上这是一个类型匹配失败错误。不过仍然可以用print_r()和var_dump()函数输出一个对象。当然,toString是可以定制的,所提供的信息和样式更丰富,如代码清单1-4所示。
- <?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();
- }
由此可见,魔术方法并不神奇。
有比较才有认知。最后,针对本节代码给出一个Java版本的代码,供各位读者用来对比两种语言中重载和魔术方法的异同,如代码清单1-5所示。
代码清单1-5 Account.java
- import org.apache.commons.lang3.builder.ToStringBuilder;
- /**
- *类的重载演示Java版本
- *@author wfox
- *@date @verson
- */
- public class Account {
- private String user;// 用户名
- private String pwd;// 密码
- public Account() {
- System.out.println("构造函数");
- }
- public Account(String user,String pwd){
- System.out.println("重载构造函数");
- System.out.println(user+"---"+pwd);
- }
- public void say(String user){
- System.out.println("用户是:"+user);
- }
- public void say(String user,String pwd){
- System.out.println("用户:"+user);
- System.out.println("密码"+pwd);
- }
- public String getUser() {
- return user;
- }
- public void setUser(String user) {
- this.user = user;
- }
- public String getPwd() {
- return pwd;
- }
- public void setPwd(String pwd) {
- }
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this);
- }
- public static void main(String…) {
- Account account=new Account();
- account.setUser("张三");
- account.setPwd("123456");
- account.say("李四");
- account.say("王五","123");
- System.out.println(account);
- }
- }
运行上述代码,输出如图1-2所示。
![]() |
| (点击查看大图)图1-2 Java里的构造方法和重载演示 |




