根据手册页之一 http://www.php.net/manual/en/language.oop5.static.php:
静态调用非静态方法会生成E_STRICT级别警告。
但是,当从类内部进行调用时
,情况似乎并非如此:error_reporting(-1);
class Test {
private $id;
function __construct($id) { $this->id = $id; }
function id() { return $this->id; }
function __toString() {
return Test::id()
. self::id()
. static::id()
. static::id()
. call_user_func('Test::id')
. call_user_func(array('Test', 'id'));
}
}
$a = new Test('a');
$b = new Test('b');
echo "$a $b $a"; # aaaaaa bbbbbb aaaaaa
var_dump(error_get_last()); # NULL
使用 php 5.4 进行测试
演示:http://codepad.viper-7.com/IKp9iX
我相信我已经证明:
- 不生成E_STRICT警告
- 该 php 神奇地将静态方法调用更正为实例方法调用(访问实例变量
id
证明了这一点)。
编辑-我想补充一点,将 debug_backtrace() 插入__toString调用会产生一个调用"类型"->
,这意味着"方法调用"。
这是一个错误,还是一个记录的功能?
如果在任何类(不仅仅是声明类)中使用静态语法调用非静态函数,则该非静态函数的行为就像在当前$this对象的上下文中调用一样。
这意味着我可以在我的类中使用其他类的方法,这些方法将假定我的类$this是他们的类$this。如果他们不检查InstanceOf
或get_class()
他们的行为将与平时完全相同。
当我像往常一样说时,我的意思是他们会假设你的类$this具有他们类$this将具有的所有其他方法和属性。
这(我认为)称为对象上下文绑定。在 JavaScript 中,你必须使用这些call()
和/或apply()
方法来让函数的行为就像它附加到某个对象一样。但是在PHP中,它只是通过在非静态函数上使用静态语法来工作。
我记得这也在C++中工作,但我不记得它是否仅在方法属于继承类时才有效(可能是您想要访问的被覆盖的方法,尽管它们被覆盖了)。
使用此(隐藏)功能,可以实现装饰器设计模式,通过该模式可以在运行时向对象添加新功能。您还可以模拟 Mixins 或 Traits(PHP 5.4 现在本机支持),额外的好处是此实现是在运行时处理的,因此可以动态完成/撤消,而不是在编译时处理的语言功能实现,并且无法动态完成/撤消,您必须更改代码才能更改功能。
我会说这是一个半文档化的功能:
在非静态上下文中,被调用的类将是 对象实例。由于 $this-> 将尝试从 相同的范围,使用 static:: 可能会给出不同的结果。另一个 不同之处在于 static:: 只能指静态属性。
换句话说(据我了解),它可以用来实现后期静态绑定:如果你在扩展测试的某个类中调用它们,使用 self::id()
和 static::id()
可能会给出不同的结果。