Press "Enter" to skip to content

PHP的Calling Scope

昨天在Yaf交流群, 大草原同学批评我变懒了, Blog很久没更新了, 今天刚好有人在Segmentfalut上问了我一个问题,  我在微博上也做了简单的解答, 不过感觉一句话说不清楚, 就写篇blog凑个数吧. 😉

问题在这里,  因为太长, 我就不copy过来了: 这是php中__call和__callStatic在被继承后会产生的bug?

这个问题乍看, 确实很容易让人迷惑, 但实际上, 造成这样的误解的根本原因在于: 在PHP中, 判断静态与否不是靠”::”(PAAMAYIM_NEKUDOTAYIM)符号, 而是靠calling scope.

那么, 什么是calling scope?

在PHP中, 调用一个方法的时候, $this指针指向的对象就是这个方法被调用时刻的calling scope. 对于下面的例子:

<?php
Foo::bar();
?>

在调用bar方法的时候, 处于一个没有calling scope域的上下文中, 所以这个是静态调用.

而对于如下的例子:

<?php
class A {
     public function test() {
         Foo::bar();
     }
 }
$a  = new A();
$a->test();

在调用bar方法的时候, 处于一个$a对象的上下文中, 也就是说, 此时的calling scope是$a对象, 所以这个其实不是静态调用.

为了验证这一个结论, 请看下面的一个实际例子:

<?php
 class Foo {
     public function bar() {
         var_dump($this);
     }
 }
 class A {
     public function test() {
         Foo::bar();
     }
 }
 $a  = new A();
 $a->test();
?>

输出什么呢?

object(A)#1 (0) {
}

在调用bar的时候, 这个看似”静态”调用的调用, $this指针却是被赋值的, 指向的是$a对象, 那么这个还算静态调用么?

我举这个例子是为了说明这个问题, 但大家在实际的应用中, 大家尽量要避免使用”::”来调用一个非静态的方法, PHP也会对于这种调用给出一个Strict 警告:

Strict Standards: Non-static method Foo::bar() should not be called statically, assuming $this from incompatible context

也许有人会说这个应该算bug吧? 其实不然, 更多的应该是错误使用造成的, 因为你在一个有calling scope的上下文中采用”静态的形式”调用了一个类的非静态方法所致.

那么PHP为什么要这么设计呢? 考虑下面的例子:

<?php
 class A {
    public function __construct() {
    }
 }
  class B extends A {
    public function __construct() {
        parent::__construct();
   }
   }

当我们调用父类的构造函数的时候, 我们是有意的要把当前的scope传递给父类的构造函数作为calling scope的.

现在大家对静态调用, 是不是稍微能有更进一步的理解呢? 下午公司马上就要开全体大会, 匆忙而就, 写的可能有点乱, 请大家海涵, 呵呵, thanks

29 Comments

  1. […] 杩欓噷瑕佽存槑鍑犵偣锛 1. 鏂瑰紡涓杩欑嶅氨鏄璇村垱寤轰竴涓瀵硅薄锛岀劧鍚庢潵璋冪敤杩欎釜瀵硅薄鐨勬柟娉曪紝杩欎釜涓嶅啿绐佺殑銆 2. 鑷充簬鏂瑰紡浜屼负浠涔堣兘澶熻繖涔堣皟鐢ㄩ潪闈欐佹柟娉曪紝杩欎釜璇峰弬鑰冮笩鍝ョ殑涓绡囨枃绔狅細PHP鐨凜alling Scope 鏂瑰紡浜岋紝浜х敓濡備笅璀﹀憡锛 鎵鏈夊ぇ瀹跺湪瀹為檯鐨勫簲鐢ㄤ腑, 澶у 灏介噺瑕侀伩鍏 浣跨敤鈥::鈥濇潵璋冪敤涓涓闈為潤鎬佺殑鏂规硶銆 […]

  2. noName
    noName 2013-05-03

    我觉得这是在PHP语言设计上有问题。

  3. FxYCarl
    FxYCarl 2013-03-15

    怪不得我之前在用call_user_function调用对象的方法的时候会出现没在对象上下文的提示…

  4. Jason
    Jason 2012-11-28

    学习了!!!

  5. x6y6
    x6y6 2012-09-21

    我有点奇怪,用调用静态方法的方法去调用一个非静态方法,为什么不干脆直接报错呢,因为按照其他语言,比如Java、C++这种情况是直接报错的。

  6. x6y6
    x6y6 2012-09-21

    用静态方法去调用一个非静态函数,这个为什么不干脆不报错呢,因为按照其他语言,比如Java、C++是直接报错的。

  7. eason
    eason 2012-07-12

    说实话,这种作用域的东西,在任何语言里都有。
    一般对javascript 运行机制,作用域,作用域链有深刻理解的人,再来看php的作用域那就更容易了。

  8. funlake
    funlake 2012-06-25

    给力,又学到东西了,感谢。

  9. PHP爱好者
    PHP爱好者 2012-06-25

    表示我是个规范的写代码的人,不过看了laruence的这篇文章,还是很有收获!

  10. wclssdn
    wclssdn 2012-06-20

    给力~~~ 不过. 看到那个$this竟然有值.. 还是A 我就诧异了….
    如果$this->bar2(); 那岂不是报bar2不存在A中?
    看来写法的规范性是必须的啊~~ 我就一直没碰到过这问题.. 哈哈哈~~~

  11. huming17
    huming17 2012-06-19

    test();
    ?>
    静态调用函数
    public function bar() 定义成 public static function bar() 再静态调用,$this指针不会被赋值。

  12. 陈辉云
    陈辉云 2012-06-19

    报不报strict,看error_reporting的设置。

  13. bill
    bill 2012-06-15

    现丑了。 最后一个例子个人觉得不能很好的说明calling scope。
    class B 的实例化对像在调用构造函数的时候,同时也会调用父类(class B)的构造函数,parent::__construct(), 这里的parent不是当前B类的实例化对像,而是Class A本身。所以这里不会是把当前的calling scope传给父类。当前的calling scope是class B的实例化对像。如果传递给父类(class A),那不就是在class A里面用class B的实例化对像(calling scope)?
    对于作用域,官方文档的OOP第章好像就有例子说明。

    轻拍

  14. wizardmin
    wizardmin 2012-06-15

    哈哈,之前也认为是个BUG
    ”::”来调用一个非静态的方法还是应该给个warning错误

  15. 子痕
    子痕 2012-06-14

    Yaf 群号: 5134185,多谢鸟哥授业解惑,哈哈。

  16. xinqiyang
    xinqiyang 2012-06-14

    鸟哥的yaf群号是啥?
    主要是parent::__construct(); 和普通的 class::staticfunction

    记住的了。。。。。

  17. jarfield
    jarfield 2012-06-14

    个人觉得最后一个parent的例子说明PHP此特性的缘由,不是很有说服力。
    Java/C++中,均有“子类构造函数调用父类构造函数”的需求。Java对super 关键字做了特殊处理。PHP也完全可以对parent::做特殊处理。类似于Foo::bar的误用,可以直接报错。
    这样是否更加安全一些?毕竟很多PHP程序员均有Java/C++的背景,某些概念深入人心。

  18. 陆离
    陆离 2012-06-14

    今天看到鸟哥的微博也觉得挺困惑的,正在纠结呢就有了这么一篇博文。学习了。

  19. 张洋
    张洋 2012-06-14

    Demon: 13:33:28
    更新blog的举动让一群活不下去的人重新站起来了!
    ……

  20. 张洋
    张洋 2012-06-14

    鸟哥V5,顶了再看…… 😛

Leave a Reply

Your email address will not be published. Required fields are marked *