PHP中的后期静态绑定究竟是什么?
你肯定需要在PHP手册中阅读Late Static Bindings。但是,我会尽量给你一个快速摘要。
基本上,它归结为self
关键字不遵循相同的继承规则这一事实。 self
总是解析为使用它的类。这意味着如果您在父类中创建一个方法并从子类调用它,self
将不会像您期望的那样引用该子类。
后期静态绑定为static
关键字引入了一个新用途,它解决了这个特殊的缺点。当你使用static
时,它代表你第一次使用它的类,即。它“绑定”到运行时类。
这是它背后的两个基本概念。当self
在场时parent
,static
和static
的运作方式可能很微妙,所以我强烈建议你研究手册页的例子,而不是详细介绍。一旦理解了每个关键字的基础知识,就可以通过这些示例来了解您将获得哪种结果。
从PHP 5.3.0开始,PHP实现了一个称为后期静态绑定的功能,可用于在静态继承的上下文中引用被调用的类。
后期静态绑定试图通过引入一个引用最初在运行时调用的类的关键字来解决该限制。决定不引入新的关键字,而是使用已经保留的static
。
我们来看一个例子:
<?php
class Car
{
public static function run()
{
return static::getName();
}
private static function getName()
{
return 'Car';
}
}
class Toyota extends Car
{
public static function getName()
{
return 'Toyota';
}
}
echo Car::run(); // Output: Car
echo Toyota::run(); // Output: Toyota
?>
late static bindings
通过存储在最后一次“非转发呼叫”中命名的类来工作。在静态方法调用的情况下,这是显式命名的类(通常是::运算符左侧的类);在非静态方法调用的情况下,它是对象的类。
“转发呼叫”是由self::
,parent::
,static::
引入的静态呼叫,或者,如果在类层次结构中上升,则为forward_static_call()
。
函数get_called_class()
可用于检索具有被调用类名称的字符串,static::
引入其范围。
行为不是很明显:
以下代码生成'alphabeta'。
class alpha {
function classname(){
return __CLASS__;
}
function selfname(){
return self::classname();
}
function staticname(){
return static::classname();
}
}
class beta extends alpha {
function classname(){
return __CLASS__;
}
}
$beta = new beta();
echo $beta->selfname(); // Output: alpha
echo $beta->staticname(); // Output: beta
但是,如果我们从beta类中删除classname函数的声明,我们会得到'alphaalpha'作为结果。
我引用了这本书:“PHP Master写的尖端代码”。
后期静态绑定是php 5.3引入的一个功能。它允许我们从父类继承静态方法,并引用被调用的子类。
这意味着您可以使用静态方法创建一个抽象类,并使用static :: method()表示法而不是self :: method()来引用子类的具体实现。
请随意查看官方的php文档:http://php.net/manual/en/language.oop5.late-static-bindings.php
解释Late Static Binding的最清晰方法是一个简单的例子。看看下面的两个类定义,继续阅读。
class Vehicle {
public static function invokeDriveByStatic() {
return static::drive(); // Late Static Binding
}
public static function invokeStopBySelf() {
return self::stop(); // NOT Late Static Binding
}
private static function drive(){
return "I'm driving a vehicle";
}
private static function stop(){
return "I'm stopping a vehicle";
}
}
class Car extends Vehicle {
protected static function drive(){
return "I'm driving a CAR";
}
private static function stop(){
return "I'm stopping a CAR";
}
}
我们看到父类(车辆)和儿童类(汽车)。父类有2种公共方法:
invokeDriveByStatic
invokeStopBySelf
父类还有2个私有方法:
drive
stop
Child Class覆盖2种方法:
drive
stop
现在让我们调用公共方法:
invokeDriveByStatic
invokeStopBySelf
问问自己:哪个班级调用invokeDriveByStatic
/ invokeStopBySelf
?家长或儿童班?
看看下面:
// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a vehicle
echo Vehicle::invokeStopBySelf(); // I'm stopping a vehicle
// This is Late Static Binding.
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// ...
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR
// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a vehicle
static
关键字用于Singleton设计模式。见链接:https://refactoring.guru/design-patterns/singleton/php/example
显示差异的最简单示例。 注意,自我:: $ c
class A
{
static $c = 7;
public static function getVal()
{
return self::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 7
后期静态绑定,请注意静态:: $ c
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
例如:
abstract class Builder {
public static function build() {
return new static;
}
}
class Member extends Builder {
public function who_am_i() {
echo 'Member';
}
}
Member::build()->who_am_i();
从“我为什么要使用它?”看它透视,它基本上是一种改变静态方法被解释/运行的上下文的方法。
使用self
,上下文是您最初定义方法的上下文。有了static
,它就是你所说的那个。
另外,请注意更新子类中的静态变量。我发现这个(有点)意外的结果,其中孩子B更新了孩子C:
class A{
protected static $things;
}
class B extends A {
public static function things(){
static::$things[1] = 'Thing B';
return static::$things;
}
}
class C extends A{
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}
print_r(C::things());
// Array (
// [2] => Thing C
// )
B::things();
print_r(C::things());
// Array (
// [2] => Thing C
// [1] => Thing B
// )
您可以通过在每个子类中声明相同的变量来修复它,例如:
class C extends A{
protected static $things; // add this and B will not interfere!
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}