Answers:
您肯定需要阅读PHP手册中的Late Static Bindings。不过,我将尝试给您一个简短的摘要。
基本上,可以归结为以下事实:self
关键字不遵循相同的继承规则。 self
始终解析为使用它的类。这意味着,如果您在父类中创建方法并从子类中调用该方法,self
则不会像您期望的那样引用该子项。
后期静态绑定为static
关键字引入了新用法,从而解决了这一特殊缺点。当您使用时static
,它代表您第一次使用它的类,即。它“绑定”到运行时类。
这些是其背后的两个基本概念。的方式self
,parent
而static
当操作static
是在打法可以是细微的,因此而不是更详细的旅途中,我强烈建议你学习手册页的例子。一旦您了解了每个关键字的基础知识,这些示例就非常有必要,以了解您将获得什么样的结果。
self
关键字不遵循继承规则。self
始终解析为使用该关键字的类。” -这并不意味着您不能self
像通过非静态方法那样通过子对象调用父对象的静态方法。您可能是对的,但您应该重新措辞。只有当孩子具有相同名称的成员时,这才真正重要,因为您可以通过使用其来决定引用哪些成员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
?>
后期静态绑定通过存储上一个“非转发调用”中命名的类来工作。在静态方法调用的情况下,这是显式命名的类(通常是
::
运算符左侧的类);在非静态方法调用的情况下,它是对象的类。A“转发呼叫”是通过引入一个静态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类中删除类名函数的声明,则结果为'alphaalpha'。
我从书中引用:“ PHP Master写最前沿的代码”。
后期静态绑定是php 5.3引入的功能。它允许我们从父类继承静态方法,并引用被调用的子类。
这意味着您可以使用静态方法创建一个抽象类,并通过使用引用子类的具体实现。 static :: method()表示法而不是self :: method()来。
随时查看官方php文档: http //php.net/manual/en/language.oop5.late-static-bindings.php
解释后期静态绑定的最清晰方法是一个简单的示例。看一下下面的两个类定义,然后继续阅读。
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";
}
}
我们看到一个父类(车辆)和一个子类(汽车)。父类有两种公共方法:
invokeDriveByStatic
invokeStopBySelf
父类还具有2个私有方法:
drive
stop
子类将覆盖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
显示差异的最简单示例。
注意,self :: $ c
class A
{
static $c = 7;
public static function getVal()
{
return self::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 7
后期静态绑定,请注意static :: $ c
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
另外,请注意是否在子类中更新静态变量。我发现了这个(某种程度上)意外的结果,其中子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;
}
}