概念
后期静态绑定这个概念平时听的很少,但是在项目中使用却相当广泛,理解了它,日常面向对象开发时将会非常顺手,关于PHP后期静态绑定,官方文档有相关的解释:
准确说,后期静态绑定工作原理是存储了在上一个“非转发调用”(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。所谓的“转发调用”(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函数来得到被调用的方法所在的类名,static:: 则指出了其范围。
该功能从语言内部角度考虑被命名为“后期静态绑定”。“后期绑定”的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为“静态绑定”,因为它可以用于(但不限于)静态方法的调用。
说的很抽象,地球人很难理解...
举个栗子
让我们先看一段代码:
class Model
{
public static function query()
{
echo "query table " . self::table() . "\n";
}
public static abstract function table();
}
class UserModel extends Model
{
public static function table()
{
return 'user';
}
}
UserModel::query();
父类Model
定义了一个query
方法,以及一个获取表名的table
抽象方法,我们希望继承它的子类来实现这个方法,定义自己的table名称,并且都能通过query
方法来获取自己的table,其中调用table
方法使用了self
关键字,我们看看这段代码会返回什么:
Fatal error: Uncaught Error: Cannot call abstract method Model::table()
代码直接报错了,并没有如愿调用UserModel
的table
方法,而是直接调用了父类的抽象方法。
非转发调用
和转发调用
-
非转发调用
指的是明确指定了类名的静态调用,比如Foo::bar()
。 -
转发调用
指的是通过self::
,parent::
,static::
,forward_static_call()
这些没有明确指定类名的静态调用,以上代码调用table
方法就是转发调用。
后期静态绑定
以上代码我们希望子类调用query
时能够自动调用自己的table
类来获取表名,很明显这里是不能直接使用非转发调用的,因为父类并不知道继承他的子类叫什么,为了实现这个逻辑,PHP引入了后期静态绑定
这个功能,并且使用了转发调用
的关键字static
来描述这个调用。
下面我们来重写父类的query
方法,将self
改成static
:
class Model
{
public static function query()
{
echo "query table " . static::table() . "\n";
}
public static abstract function table();
}
再次调用UserModel::query()
,得到了我们想要的返回:
query table user
再举一个例子,最常用的单例模式,在父类中封装一个实例化对象的方法我们可以直接使用new static()
来实现:
abstract class Factory
{
public static $instance = null;
public static function getInstance()
{
if (is_null(self::$instance)) {
self::$instance = new static();
}
return self::$instance;
}
}