PHP 5.3以后引入的一个叫做后期静态绑定的功能,用于在继承范围类引用静态调用的类。
这句话可以这么理解,看代码示例:
示例A:
<?php
class BaseTest {
public static function test()
{
echo 'hello test';
}
}
class Statictest extends BaseTest {
public function myIsTest()
{
echo __METHOD__;
}
}
Statictest::test();
结果输出:hello test
所谓的静态调用的类指的是这边的 Statictest
后期静态绑定PHP官网解释是这样的:后期静态绑定工作原理是存储了在上一个“非转发调用”(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。可以通过 get_called_class()
函数来得到被调用的方法所在的类名,static:: 则指出了其范围,实现后期静态绑定。
所谓的“转发调用”(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。
当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分) 我们通过示例A
来理解,即 Statictest
。
当进行非静态方法调用时,即为该对象所属的类。我们看示例B
来理解。
示例B:
<?php
class BaseTest {
public static function test()
{
echo __METHOD__;
}
}
class Statictest extends BaseTest {
public function myIsTest()
{
echo __METHOD__;
}
}
$statictest = new Statictest();
$statictest->test();
输出:BaseTest::test //按后期静态绑定的预期的是这边需要输出的应该是对象所属的类,即 Statictest::test,但是实际上输出的是BaseTest::test
“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。
先说明下self::的限制
使用 self:: 或者 _CLASS_ 对当前类的静态引用,取决于定义当前方法
所在的类(而不是后期静态绑定所描述的实际运行时计算):
示例C:
<?php
class Base {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class Test extends Base {
public static function who() {
echo __CLASS__;
}
}
Test::test();
输出:Base
后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test()
时引用的类是 Test
而不是 Base
。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。
后期静态绑定的基本用法:
示例D:
<?php
class Base {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); //后期静态绑定开始
}
}
class Test extends Base {
public static function who() {
echo __CLASS__;
}
}
Test::test();
输出: Test
非静态调用的方法:
在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。
示例E:
<?php
class Base {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo();
}
}
class Test1 extends Base {
}
class Test2 extends Base {
private function foo() {
echo 'foo success';
}
}
$test1 = new Test1();
$test1->test();
$test2 = new Test2();
$test2->test();
输出:success! success! success!
Fatal error: Uncaught Error: Call to private method Test2::foo() from context 'Base'
注意:关于$this-> 会在同一作用范围内尝试调用私有方法的理解
示例F:
<?php
class Test {
public function mtest()
{
$this->vtest();
}
public function vtest()
{
echo 'my is Test vtest';
}
}
class Ztest extends Test{
public function vtest()
{
echo 'My is Ztest vtest';
}
}
$ztest = new Ztest;
$ztest->mtest();
输出:My is Ztest vtest //方法属性为public,输出当前类的方法
示例G:
<?php
class Test {
public function mtest()
{
$this->vtest();
}
private function vtest()
{
echo 'my is Test vtest';
}
}
class Ztest extends Test{
private function vtest()
{
echo 'My is Ztest vtest';
}
}
$ztest = new Ztest;
$ztest->mtest();
输出:my is Test vtest // 方法属性为private
转发和非转发调用
后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。
示例H:
<?php
class Base {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class Ztest extends Base {
public static function test() {
Base::foo(); //静态调用,非转发调用 官网定义中的:当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分)
parent::foo(); //转发调用,调用父类(Base)中的foo方法,进行后期静态绑定
self::foo(); // 转发调用,可参考示例C,调用Base中的foo方法,进行后期静态绑定(后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止)
}
public static function who() {
echo __CLASS__."\n";
}
}
class Vtest extends Ztest {
public static function who() {
echo __CLASS__."\n";
}
}
Vtest::test();
输出:Base Vtest Vtest