0 Comments

 toString方法

发布于:2012-12-27  |   作者:广州网站建设  |   已聚集:人围观

    看另外一个魔术方法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所示。


代码清单1-4 magic_2.php广州网站建设


  1. <?php 
  2. class Account{  
  3. public $user=1;  
  4. private $pwd=2;  
  5. // 自定义的格式化输出方法  
  6. public function toString(){  
  7. return "当前对象的用户名是{$this->user},密码是{$this->pwd}";  
  8. }  
  9. }  
  10. $a=new Account();  
  11. echo $a;  
  12. echo PHP_EOL;  
  13. print_r($a); 

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

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


  1. ZEND_VM_HANDLER(40, ZEND_ECHO, CONST|TMP|VAR|CV, ANY)  
  2. {  
  3. zend_op *opline = EX(opline);  
  4. zend_free_op free_op1;  
  5. zval z_copy;  
  6. zval *z = GET_OP1_ZVAL_PTR(BP_VAR_R);  
  7. // 此处的代码预留了把对象转换为字符串的接口  
  8. if (OP1_TYPE != IS_CONST &&  
  9. Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_method != NULL &&  
  10. zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) {  
  11. zend_print_variable(&z_copy);  
  12. zval_dtor(&z_copy);  
  13. } else {  
  14. zend_print_variable(z);  
  15. }  
  16.  
  17. FREE_OP1();  
  18. ZEND_VM_NEXT_OPCODE();  

由此可见,魔术方法并不神奇。

有比较才有认知。最后,针对本节代码给出一个Java版本的代码,供各位读者用来对比两种语言中重载和魔术方法的异同,如代码清单1-5所示。

代码清单1-5 Account.java


  1. import org.apache.commons.lang3.builder.ToStringBuilder;  
  2. /**  
  3. *类的重载演示Java版本  
  4. *@author wfox  
  5. *@date @verson  
  6. */  
  7. public class Account {  
  8. private String user;// 用户名  
  9. private String pwd;// 密码  
  10.  
  11. public Account() {  
  12. System.out.println("构造函数");  
  13. }  
  14. public Account(String user,String pwd){  
  15. System.out.println("重载构造函数");  
  16. System.out.println(user+"---"+pwd);  
  17. }  
  18. public void say(String user){  
  19. System.out.println("用户是:"+user);  
  20. }  
  21. public void say(String user,String pwd){  
  22. System.out.println("用户:"+user);  
  23. System.out.println("密码"+pwd);  
  24. }  
  25.  
  26. public String getUser() {  
  27. return user;  
  28. }  
  29. public void setUser(String user) {  
  30. this.user = user;  
  31. }  
  32. public String getPwd() {  
  33. return pwd;  
  34. }  
  35. public void setPwd(String pwd) {  
  36. }  
  37. @Override  
  38. public String toString() {  
  39. return ToStringBuilder.reflectionToString(this);  
  40. }  
  41.  
  42. public static void main(String…) {  
  43.  
  44. Account account=new Account();  
  45. account.setUser("张三");  
  46. account.setPwd("123456");  
  47. account.say("李四");  
  48. account.say("王五","123");  
  49. System.out.println(account);  
  50. }  

 

运行上述代码,输出如图1-2所示。
(点击查看大图)图1-2 Java里的构造方法和重载演示
       可以看出,Java的构造方法比PHP好用,PHP由于有了set/get这一对魔术方法,使得动态增加对象的属性字段变得很方便,而对Java来说,要实现类似的效果,就不得不借助反射API或直接修改编译后字节码的方式来实现。这体现了动态语言的优势,简单、灵活。
飞机