最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • PHP类的秘密(七) 下钩子hooks与魔术方法

    正文概述 转载于:掘金(传达室老大爷)   2021-06-29   562

    这是我参与更文挑战的第10天,活动详情查看: 更文挑战

    下钩子hooks与魔术方法

    php类的定义中, 还有一种特殊的成员方法, 他们有特殊的触发方式, 以实现一些特殊的应用, 他们以双下划线开头"__", 称为魔术方法, 我们今天就来看看魔术方法以及他们是如何下钩子的.

    钩子函数

    所谓钩子函数, 是在正常的程序执行过程中, 插入一个钩点(hook), 然后通过这个钩点, 先去执行钩子函数, 之后再回来执行主程序, 或者干脆不执行主程序. 简单地讲,就是“要想从这过,留下买路财".

    PHP类的秘密(七) 下钩子hooks与魔术方法

    在PHP中将这种下钩子的行为成为"overloading", 不过这和我们普通意义上的overloading是两回事儿, 传统的重载是用于提供多个同名的类方法,但各方法的参数类型和个数不同。而PHP其实是不支持重载的!

    所以在官方文档介绍overloading的时候, PHPer们更倾向于把它称作"下钩子"(hook).

    PHP中是通过魔术方法来实现下钩子的: 简单讲, 就是在类定义时, 加进一些魔术方法, 这些魔术方法会针对一些特殊的关键词, 比如new, echo, var_export() 或者当调用一些不可访问的属性和方法时, 自动触发, 实现一些非常规的输出结果.

    我们来详细介绍一些常见的魔术方法.

    __toString()

    这个魔术方法可以允许一个类去决定, 当它被当作一个字符串时, 如何响应. 比如说"echo $obj;"时要输出什么.

    如果类中没有定义__toString()方法, 直接打印对象的话, 会报错. 如果定义了该方法, 那么当使用echo/print/printf打印对象时, 都会调用__toString();

    举个例子:

    class ABC
    {
        private $ab = 'DIT';
        public $cd= '123';
        protected $ef='true';
    
        public function __toString()    
        {
            return (string)var_dump($this);   //将结果转换为字符串格式
        }
    }
    $bba=new ABC();
    echo $bba;  //output:object(ABC)#5 (3) {
      ["ab":"ABC":private]=>
      string(3) "DIT"
      ["cd"]=>
      string(3) "123"
      ["ef":protected]=>
      string(4) "true"}
    

    在使用__toString()时, 返回值必须是字符串类型, 否则会报错. 另外这个方法只有在打印对象时会被调用, 打印其他类型对象时不会被调用.

    __set_state()

    这个魔术方法可以让一个对象的属性赋给另一个对象, 不过使用起来稍微有些复杂:

    1. 输出, 首先当调用 var_export() 导出对象$a时,此静态方法会被调用. 比如var_export($a, true)返回值是一个字符串, 是关于变量$a的结构信息.

    2. 转存, 然后__set_state(array $properties)会将$a的属性自动存放到$properties这个数组当中, 属性名=>属性值的格式, 但此时, 整个函数的表达式仍然是字符串格式, 是无法执行的.

    3. 执行, 将var_export()放到eval()函数中, 执行整个语句.

    我们来看一下例子:

    class A
    {
        public $var1;
        public $var2;
    
    public static function __set_state($an_array) 
    {
        $obj = new A;
        $obj->var1 = $an_array['var1'];
        $obj->var2 = $an_array['var2'];
        return $obj;
    }
    }
    
    $a = new A;
    $a->var1 = 5;
    $a->var2 = 'foo';
    eval('$b = ' . var_export($a, true) . ';');
    var_dump($b); 
    //output: object(A)#5 (2) {
      ["var1"]=>
      int(5)
      ["var2"]=>
      string(3) "foo"}
      
    

    我们来一步步分析一下:

    1. 输出, var_export($a, true), 会返回以下字符串:

       string(60) "A::__set_state(array(
      'var1' => 5,
      'var2' => 'foo',
       ))"
      
    2. 转存, 输出的语句调用了__set_state($an_array)方法, 并将$a的两个属性作为元素, 放到了$an_array数组中, 并按照魔术方法, 对临时变量$obj对象的属性进行了赋值;

    3. 执行, 通过eval('$b = ' . var_export($a, true) . ';');执行var_export()输出的字符串, 并进行赋值$b, 所以最后$b的数据结构是对象类型;

    如果没有eval()去执行的话, 直接改为$b=var_export($a, true), 那么$b会是字符串类型, 大家可以在本地验证一下.

    最后注意在声明这个魔术方法的时候, 一定要加static关键词, 因为它是静态的.

    __debugInfo()

    这个魔术方法的作用是: 当调用var_dump()输出一个对象时, 这个方法会被调用来显示应该被展示的属性. 他的返回类型是数组.

    __debugInfo(): array

    当然, 你可以通过这个方法, 返回任意内容的数组. 我们来看个例子:

    class C {
        private $prop;
    
        public function __construct($val) 
        {
            $this->prop = $val;
        }
    
        public function __debugInfo() {  
        return ['result'=>$this->prop*2];
        }
    }
    
    var_dump(new C(42));  //object(C)#6 (1) { ["result"]=>int(84) }
    

    通过这个魔术方法, 阻断了var_dump打印所有属性, 只输出了指定的属性, 并对属性值进行了修改.

    构造与析构函数

    __construct(), __destruct()

    对, 我们在本系列之前专门用一章介绍过这两个函数, 他们会在创建对象时自动调用, 为对象属性初始化进行初始化赋值, 因为其是创建对象时第一个运行的方法, 还可以实现一些其他功能, 比如打印欢迎语.

    而析构函数, 是当对象的所有引用都被删除, 或者程序已经运行完毕后, 自动运行, 清理对象释放内存.

    详细的介绍可以看PHP类的秘密(五) 构造函数详解, 在这里就不再过多介绍了.

    几个不太建议使用的魔术方法

    属性重写

    _`_set(string $name, mixed $value) 在给不可访问属性赋值时,__set() 会被调用。

    __get(string $name) 读取不可访问属性的值时,__get() 会被调用。

    __isset(string $name), 当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用。

    __unset(string $name), 当对不可访问属性调用 unset() 时,__unset() 会被调用。`

    方法重写

    `__call(string $name, array $arguments) 在对象中调用一个不可访问方法时,__call() 会被调用。

    static __callStatic(string $name, array $arguments) 在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。`

    为什么说这几个魔术方法不太建议使用呢? 因为这几个方法会严重影响代码的预期结果, 并且会导致花更多的时间去debug, 并且在结对编程, 代码集成的时候, 发生难以预料的bug和问题.

    举个例子:

    class Student
    {
        private $a;
        private $b=5;
        protected $d=10;
    
        function __get($name)
        {
            return 123;
        }
    
        function __set($name, $value) {}
        function __call($n, $m){}
        private function veryPrivateMethod(){}
    }
    $s=new Student;
    $s->a=10   //从类外访问私有属性
    var_dump($s->a)     //int(123), 调用__get($name);
    $s->veryPrivateMethod()   //类外调用私有成员方法, 调用__call($n, $m), 无报错信息
    

    可以看到, 上例中通过这些魔术方法, 将PHP类的规范完全颠覆了, 几乎可以为所欲为. 系统失去了判断能力, 自废了武功. 而且输出的结果会让人非常迷惑. 在这里强烈建议大家不要使用这些魔术方法.

    感谢阅读, 如有不准确和错误的地方请留言指正, 我会及时修正, 拜谢!

    总结不易, 请勿私自转载, 否则别怪老大爷不客气!

    欢迎各位爱研究的小伙伴儿和我交流, 互相学习, 一起成长!

    参考资料:

    WWW.PHP.NET PHP官方文档

    程序员们之间常说的钩子“hook”是啥意思?


    起源地下载网 » PHP类的秘密(七) 下钩子hooks与魔术方法

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元