PHP 中的后期静态绑定到底是什么?
8 回答
您肯定需要阅读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
?>
后期静态绑定通过存储在最后一个“非转发调用”中命名的类来工作。在静态方法调用的情况下,这是明确命名的类(通常是运算符左侧的类
::
);在非静态方法调用的情况下,它是对象的类。“转发呼叫”是由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 大师编写尖端代码”。
后期静态绑定是 php 5.3 引入的一个特性。它允许我们从父类继承静态方法,并引用被调用的子类。
这意味着您可以拥有一个带有静态方法的抽象类,并通过使用 static::method()表示法而不是 self::method() 来引用子类的具体实现。
也可以随意查看官方 php 文档: http: //php.net/manual/en/language.oop5.late-static-bindings.php
解释后期静态绑定的最清晰方法是使用一个实际示例。我在模板方法模式中使用它。见下文。
abstract class AbstractTemplate {
public const AWESOME_LIST = [''];
public function someFunction(): void {
$awesomeList = $this->getAwesomeList();
// OUTPUT: ['harry','henk','john'];
var_dump($awesomeList);
}
/**
* This function gets static constants from CHILD classes
*/
public function getAwesomeList(): array
{
return static::AWESOME_LIST;
}
}
class ConcreteTemplate extends AbstractTemplate {
public const AWESOME_LIST = ['harry','henk','john'];
public function someFunction(): void {
parent::someFunction();
}
}
$concreteTemplate = new ConcreteTemplate();
$concreteTemplate->someFunction();
注意static
method 中的关键字getAwesomeList
。现在让我们稍微改变一下:
public function getAwesomeList(): array
{
return self::AWESOME_LIST;
}
var_dump
at的输出someFunction
将是:
array (size=1)
0 => string '' (length=0)
该static
关键字用于单例设计模式。见链接: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
后期静态绑定,注意static::$c
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
从“我为什么要使用它?”的角度来看它。从角度来看,它基本上是一种更改解释/运行静态方法的上下文的方法。
使用self
,上下文是您最初定义方法的上下文。使用static
,它就是你调用它的那个。
例如:
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();
另外,请注意是否更新子类中的静态变量。我发现了这个(有点)意外的结果,孩子 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;
}
}