PHP 中的魔术方法

痛定思痛。 2022-03-08 07:10 489阅读 0赞

PHP 中魔术方法是以下两个划线 “__” 开头的、具有特殊作用的一些方法,可以看作PHP的“语法糖”。

语法糖是指那些没有给计算机语言添加新功能,而只是对人类来说更 “甜蜜” 的语法。语法糖往往给程序员提供了更实用的编码方式或者一些技巧性的用法,有益于更好地编码风格,使代码更易读。不过其并没有给语言添加什么新的东西。PHP 里引用、SPL 等都属于语法糖。

1. __construct 方法

__construct() 是一个标准的魔术方法,所以非常适合在使用对象之前做一些初始化工作。因此这个方法往往用于类进行初始化时执行一些初始化操作。给属性赋值、连接数据库等。

  1. class Girlfriend{
  2. public $name;
  3. public $height;
  4. public function __construct($name, $height){
  5. $this->name = $name;
  6. $this->height = $height;
  7. }
  8. }
  9. $myGirl = new Girlfriend('Lucy', '170cm');

2. __set 和 __get 方法

类的属性一半都设置为私有属性,再通过 public 的方法去修改这个属性,这种做法体现了 将数据与行为进行分离 的封装思想。

请看下面一段代码:

  1. class Son{
  2. private $name;
  3. private $age;
  4. private $gender;
  5. public function __set($name, $value){
  6. $this->name = $value;
  7. }
  8. public function __get($name){
  9. if(!isset($this->$name)){
  10. echo $name . '未设置';
  11. $this->$name = '这是帮您设置的默认值';
  12. }
  13. return $this->$name;
  14. }
  15. }
  16. $whp = new Son();
  17. $whp->name = 'wangba';
  18. $whp->age = '10';
  19. $whp->gender = '男';
  20. echo '姓名:', $whp->name, ' 性别:', $whp->gender, ' 年龄:', $whp->age;

使用这两个魔术方法在对类的私有属性进行操作时,不需要显示地调用一个 public 的方法 ,显得更为方便。

3. __call() 和 _callStatic 方法

当调用一个不可访问的方法(如未定义,或者不可见)时,__call() 会被调用。其中 $method 参数是需要调用的方法名称。$arguments 参数是一个数组,包含着要传递给方法的参数,如下所示:

  1. class Test{
  2. public function __call($method, $arguments){
  3. switch(count($arguments)){
  4. case 2:
  5. echo $arguments[0] * $arguments[1], PHP_EOL;
  6. break;
  7. case 3:
  8. echo array_sum($arguments), PHP_EOL;
  9. break;
  10. default:
  11. echo '参数不对', PHP_EOL;
  12. break;
  13. }
  14. }
  15. }
  16. $test = new Test();
  17. $test->sum(1);
  18. $test->sum(2, 3);

以上代码模拟了类似其他语言根据参数类型进行重载。Test 类中 sum 方法并不存在,当对象 $test 调用 sum 方法时,Test 类中的魔术方法 __call 会被调用,参数即为 $test调用sum 时传的参数,例如第一次调用 $arguments = 1; 第二次调用 $arguments = array(2, 3);

跟 __call 配套的魔术方法是 __callStatic,当调用的静态方法不存在时 __callStatic 将会被调用。

当然,使用魔术方法“防止调用不存在的方法而报错”,并不是魔术方法的本意。实际上,魔术方法是方法的动态创建成为可能,这在 MVC 等框架设计中是 很有用的语法。假设一个控制器调用了一个不存在的方法,那么只要定义了 __call 魔术方法,就能友好的处理这种情况。

4. __toString 和 __debugInfo 方法

当进行测试时,需要知道是否得出正确的数据。比如打印一个对象时,看看这个对象都有哪些属性,其值是神么。如果类定义了 __toString 方法,就能在测试时,echo 打印对象体,对象就会自动调用他所属类定义的 __toString 方法,格式化输出这个对象所包含的数据。如果没有这个方法,echo 一个对象将会报错。也可以用 print_r() 或者是 var_dump() 函数输出一个对象。当然, __toString 是可以定制的,所提供的信息和样式更丰富,例如:

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

为什么直接 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. if (OP1_TYPE != IS_CONST &&
  8. Z_TYPE_P(z) == IS_OBJECT && Z_OBJ_HT_P(z)->get_method != NULL &&
  9. zend_std_cast_object_tostring(z, &z_copy, IS_STRING TSRMLS_CC) == SUCCESS) {
  10. zend_print_variable(&z_copy);
  11. zval_dtor(&z_copy);
  12. } else {
  13. zend_print_variable(z);
  14. }
  15. FREE_OP1();
  16. ZEND_VM_NEXT_OPCODE();
  17. }

PHP 在 echo 一个对象时,会判断变量类型是否为 string,如果不是 string 的话,则去寻找可以转换为 string 的方法。在寻找到对象中 __toString方法时,调用 zend_std_cast_object_tostring 方法。

发表评论

表情:
评论列表 (有 0 条评论,489人围观)

还没有评论,来说两句吧...

相关阅读

    相关 PHP5魔术方法

    魔术函数 1。\_\_construct() 实例化对象时被调用, 当\_\_construct和以类名为函数名的函数同时存在时,\_\_construct将被调用,

    相关 PHP 魔术方法

    PHP 中魔术方法是以下两个划线 “\_\_” 开头的、具有特殊作用的一些方法,可以看作PHP的“语法糖”。 > 语法糖是指那些没有给计算机语言添加新功能,而只是对人类来说更