自 PHP 5.3.0 起,PHP 增长了一个叫作后期静态绑定的功能,用于在继承范围内引用静态调用的类。 虽然也能够调用非静态方法,可是不会在运行时绑定。php
static 再也不只是简单的静态修饰关键字。而是还能够调用类的静态方法,非静态方法,为何静态非静态要分开说呢,由于调用的效果是不同的。函数
class A { protected $name = 'A'; static $alias = 'a'; const HASH = 'md5'; public function dd() { echo 'name:' . $this->name . PHP_EOL; echo 'self-alias:' . self::$alias . PHP_EOL; echo 'static-alias:' . static::$alias . PHP_EOL; // 后期静态绑定 echo 'self-hash:' . self::HASH . PHP_EOL; echo 'static-hash:' . static::HASH . PHP_EOL; // 后期静态绑定 var_dump(new self); var_dump($this); var_dump(new static); } public static function who () { echo __CLASS__ ; echo ' [ This is A ]'; echo PHP_EOL; } public static function test () { static:: who (); // 后期静态绑定从这里开始 } public static function test2 () { self:: who (); } } class B extends A { protected $name = 'B'; static $alias = 'b'; const HASH = 'sha1'; public static function who () { echo __CLASS__ ; echo ' [ This is B ]'; echo PHP_EOL; } } (new B)->dd()
结果输出:this
name:B self-alias:a static-alias:b self-hash:md5 static-hash:sha1 object(admin\controllers\A) protected 'name' => string 'A' (length=1) object(admin\controllers\B) protected 'name' => string 'B' (length=1) object(admin\controllers\B) protected 'name' => string 'B' (length=1)
执行:code
B::who(); B::test(); B::test2();
输出:对象
B [ This is B ] B [ This is B ] A [ This is A ]
self
和 __CLASS__
,都是对当前类的静态引用
,取决于定义当前方法所在的类。也就是说,self 写在哪一个类里面, 它引用的就是谁。继承
$this
指向的是实际调用时的对象,也就是说,实际运行过程当中,谁调用了类的属性或方法,$this
指向的就是哪一个对象。但 $this
不能访问类的静态属性和常量,且 $this
不能存在于静态方法中。md5
static
关键字除了能够声明类的静态成员(属性和方法)外,还有一个很是重要的做用就是后期静态绑定。get
parent
,是对当前类的父类的静态引用。string
self
能够用于访问类的静态属性、静态方法和常量,但 self
指向的是当前定义所在的类,这是 self
的限制。hash
$this
指向的对象所属的类和 static
指向的类相同。
static
能够用于静态或非静态方法中,也能够访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性。
静态调用时,static
指向的是实际调用时的类;非静态调用时,static
指向的是实际调用时的对象所属的类。
后期静态绑定(也叫延迟静态绑定),可用于在继承范围内引用静态调用的类,也就是代码运行时最初调用的类。
确切地说,static 后期静态绑定的工做原理是存储了上一个非转发调用(non-forwarding call)的类名。
当进行静态方法调用时,该类名(static指向的类名)为明确指定的那个(一般是 :: 运算符的左侧部分),即实际调用时的类。
如:上面例子中的
A::test(); //A::test() 调用的是 static::who(),这里static指向的即是A,因此执行的就是A::who(); B::test(); //A::test() 调用的是 static::who(),这里static指向的即是B,因此执行的就是B::who();
static指向的类名,指向的就是实际调用的类
self 能够用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制。
static 也能够用于访问类的静态属性、静态方法和常量,static 指向的是实际调用时的类。当进行非静态方法调用时,该类名(static指向的类名)为该对象所属的类,即实际调用时的对象所属的类。
所谓的转发调用(forwarding call)指的是经过如下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call() 。
可用 get_called_class() 函数来获取被调用的方法所在的类名。
如下四种形式的调用,都是转发调用:
self:: parent:: static:: forward_static_call()
除此以外的调用,就是非转发调用。
后期静态绑定的工做原理是存储了上一个非转发调用(non-forwarding call)的类名。
经过具体的类名或具体的对象进行的调用都是非转发调用。
非静态环境下的私有方法的查找顺序
在非静态环境下,在类的非静态方法中,使用 $this 和 static 调用类的私有方法时,执行方式有所不一样。
具体来讲,$this 会先到所在定义范围内寻找私有方法,再到它指向的对象所属的类中寻找私有方法,而后寻找公有方法,最后到所在定义范围内寻找公共方法。只要找到了匹配的方法,就调用,并中止查找。
而 static 则是先到它指向的类中寻找私有方法,再寻找共有方法;而后到所在定义范围内寻找私有方法,再寻找共有方法。只要找到了匹配的方法,就调用,并中止查找。
下面是一个例子:
<?php class A { private function foo () { var_dump($this); echo '--'; var_dump(new static); echo '--'; echo __CLASS__; echo '--'; echo get_called_class(); echo '<br>'; } public function test () { $this -> foo (); static:: foo (); echo '<br>'; } } class B extends A { } class C extends A { private function foo () { echo 'this is C'; } } (new B())->test(); (new C())->test(); 输出结果为: object(B)#1 (0) { } --object(B)#2 (0) { } --A--B object(B)#1 (0) { } --object(B)#2 (0) { } --A--B object(C)#1 (0) { } --object(C)#2 (0) { } --A--C Fatal error: Uncaught Error: Call to private method C::foo() from context 'A'
后期静态绑定的解析会一直到取得一个彻底解析了的静态调用为止。若是静态调用使用了 parent:: 或者 self:: 等转发调用的形式,将会转发调用信息。
<?php class A { public static function foo () { static:: who (); } public static function who () { echo __CLASS__ . "\n" ; } } class B extends A { public static function test () { A :: foo (); parent :: foo (); self :: foo (); static::foo(); forward_static_call(['A', 'foo']); echo '<br>'; } public static function who () { echo __CLASS__ . "\n" ; } } class C extends B { public static function who () { echo __CLASS__ . "\n" ; } public static function test2() { self::test(); } } class D extends C { public static function who () { echo __CLASS__ . "\n" ; } } B::foo(); B::test(); C::foo(); C::test(); D::foo(); D::test2();
以上的输出结果为:
B A B B B B C A C C C C D A D D D D
static 后期静态绑定的工做原理是存储了上一个非转发调用(non-forwarding call)的类名。请记住这句话。
下面的例子是非转发调用。
A::foo(); // 输出 A B::foo(); // 输出 B C::foo(); // 输出 C
后期静态绑定 static ,是定义在了 foo() 方法中,哪一个类经过非转发调用的形式调用 foo() 方法, foo() 方法中的 static 指向的就是哪一个类。
可是,若是经过转发调用的形式,调用 foo() 方法,如:
parent :: foo (); self :: foo (); static::foo(); forward_static_call(['A', 'foo']);
那么,就以转发调用代码所在的方法 test() 为准,哪一个类经过非转发调用的形式调用 test() 方法, foo() 方法中的 static 指向的就是哪一个类。
假如调用 test() 方法时,也采用了转发调用的形式,如:
public static function test2() { self::test(); }
那么,就以 test2() 方法为准 ... 依次类推。
也就是说,在使用了后期静态绑定的基类中,后期静态绑定所在的方法若是被转发调用,则 static 的指向,会一直向上追溯,直到遇到非转发调用的形式。