PHP中的后期静态绑定究竟是什么?
答案 0 :(得分:184)
您肯定需要阅读PHP手册中的Late Static Bindings。但是,我会尽量给你一个快速摘要。
基本上,它归结为self
关键字不遵循相同的继承规则这一事实。 self
始终解析为使用它的类。这意味着如果您在父类中创建一个方法并从子类中调用它,self
将不会像您期望的那样引用该子类。
后期静态绑定为static
关键字引入了一种新用法,它解决了这个特殊的缺点。当您使用static
时,它代表您首次使用它的类,即。它“绑定”到运行时类。
这是它背后的两个基本概念。 self
在parent
发挥作用时static
,static
和{{1}}的运作方式可能很微妙,所以我强烈建议您学习,而不是详细介绍手册页示例。一旦了解了每个关键字的基础知识,就可以通过这些示例来了解您将获得哪种结果。
答案 1 :(得分:78)
从PHP 5.3.0开始,PHP实现了一个名为late static binding的功能,可用于在静态继承的上下文中引用被调用的类。
后期静态绑定试图通过引入一个引用最初在运行时调用的类的关键字来解决该限制。决定不引入新关键字,而是使用已保留的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::
引入其范围。
答案 2 :(得分:22)
没有非常明显的行为:
以下代码生成'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'作为结果。
答案 3 :(得分:10)
我引用了这本书:“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
{1}}关键字用于Singleton设计模式。 请参见链接:https://refactoring.guru/design-patterns/singleton/php/example
答案 4 :(得分:7)
显示差异的最简单的例子 注意, 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
答案 5 :(得分:4)
例如:
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();
答案 6 :(得分:4)
从“我为什么要使用它?”看它透视,它基本上是一种改变静态方法被解释/运行的上下文的方法。
使用self
,上下文是您最初定义方法的上下文。使用static
,就是你要来的那个。
答案 7 :(得分:1)
此外,请注意是否在子类中更新静态变量。我发现这(某种程度上)出人意料的结果是子B更新了子C:
showroom.getTextContent()
您可以通过在每个子类中声明相同的变量来解决此问题,例如:
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
// )