注:

延迟静态绑定的解析将在完全解析的静态停止 呼叫时不带任何后备.另一方面,使用关键字的静态调用 大概有parent:: or self:: will forward the calling information个.

示例#4前转和非前转呼叫

https://www.php.net/manual/en/language.oop5.late-static-bindings.php

<?php
class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo(); // what? - Why is resolved to C if B's father is A?
        self::foo(); // what? - Why is it not resolved in B?
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();
?>

输出:

A
C
C

我不明白parentself在这个例子中的用法,你能解释一下吗?

推荐答案

在每次调用静态方法期间,PHP运行时都知道两条信息:

  • calling context-通常是指called方法时引用的类;这就是static::所指的类
  • 方法的实际definition所在的类;这就是self::所指的类,也是魔力常数__CLASS__将解析到的类

让我们先看一个更简单的例子:

class A {
   public static function test() {
       echo 'Defined in ', self::class, '; called in ', static::class, "\n";
   }
}
class B extends A {
}

呼叫A::test();将输出Defined in A; called in A-self,而static指的是同一类.

调用B::test();将输出Defined in A; called in B-尽管在类B中没有定义名为test()的方法,但PHP仍然知道您在引用B时调用了它.


当您多次使用self::时,就会出现"转发"--PHP会跟踪original调用上下文:

class A {
   public static function test() {
       echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
   }
   public static function forward_test() {
       echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
       self::test();
   }
}
class B extends A {
    public static function test() {
        echo 'this method is not called from forward_test()';
    }
}
B::forward_test();

输出:

forward_test defined in A; called in B
test defined in A; called in B

这里发生的事情是这样的:

  • PHP将调用上下文设置为类B,并查找方法forward_test()
  • forward_test中的definition在类A中,并输出我们的第一行调试
  • Now forward_test calls self::test(), and two things happen
    • test中的definitionclass A中查找,因为这是forward_test中的definition
    • butcalling context保留为B类,因为self调用"转发"该信息
  • 因此调用了A::test(),但调用上下文为B,导致我们的第二行调试

在内部,您可以想象编译器将每个方法调用替换为需要目标类名、方法名和调用上下文的call_method函数.

对于self::test(),它可以立即用当前类A替换self,并输出类似以下内容:

call_method(targetClass: 'A', methodName: 'test', callingContext: $currentCallingContext)

只有当它运行时,$currentCallingContext才被定义和转发.

A::test()的显式调用显式定义了目标类and和调用上下文:

call_method(targetClass: 'A', methodName: 'test', callingContext: 'A')

相反,对static::test()的"后期静态绑定"调用基于调用上下文定义目标类:

call_method(targetClass: $currentCallingContext, methodName: 'test', callingContext: $currentCallingContext)

同样的情况也发生在问题中示例中的parent个调用中:

  • 我们呼叫C::test()
  • 定义在B中,但调用上下文是C
  • parent::foo()的呼叫解析到A中的definition,但calling context被转接,C也是如此
  • 因此,我们运行A::foo(),调用上下文为C
  • 然后我们调用static::who(),它查看calling context以决定运行哪个方法("后期静态绑定"),因此它运行C::who()
  • 我们现在处于类C中定义的方法中,魔术常量__CLASS__是字符串'C'

下面是一个扩展示例,其中显示了更多信息和更多变体:

class A {
    public static function foo() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        static::who();
    }

    public static function who() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function foo() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        static::who();
    }
    
    public static function test() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        echo "A::foo():\n";
        A::foo();
        echo "B::foo():\n";
        B::foo();
        echo "C::foo():\n";
        C::foo();
        echo "parent::foo():\n";
        parent::foo();
        echo "self::foo():\n";
        self::foo();
    }

    public static function who() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __FUNCTION__,  ' defined in ', self::class, '; called in ', static::class, "\n";
        echo __CLASS__."\n";
    }
}

C::test();

输出:

test defined in B; called in C
A::foo():
foo defined in A; called in A
who defined in A; called in A
A
B::foo():
foo defined in B; called in B
who defined in B; called in B
B
C::foo():
foo defined in B; called in C
who defined in C; called in C
C
parent::foo():
foo defined in A; called in C
who defined in C; called in C
C
self::foo():
foo defined in B; called in C
who defined in C; called in C
C

Php相关问答推荐

不包括WooCommerce中单独销售的产品

WooCommerce常规价格增加额外的附加平价

laravel中获取的数据会自动更改时间戳值

在WordPress中从CPT中删除插件

使用HPOS过滤WooCommerce中的订单列表

强制客户在注册时 Select WooCommerce角色

如何批量处理消息总线中的消息

PHP 8.2 弃用日志(log)存储在 Laravel 10.22.0 中的哪里?

如何用十进制和字母数字减go 和添加 id

将产品数量、名称和结帐链接添加到 WooCommerce 添加到购物车消息

使用 phpseclib 验证 RSA-PSS

从插件更改主题功能,function_exists 不起作用

图像未在 Laravel Ajax Crud 中显示

$_SERVER 超全局变量在 PHP 中是否保证可写?

php/html OnSubmit 禁用输入类型提交未给出预期结果

为 WordPress 页面创建新功能

使用 splat 运算符时按引用传递 (...)

如何通过本地主机在同一台 PC 上同时运行 Microsoft IIS 和 WAMP

PHP 从 API 获取随机项目

如何在不复制的情况下在php中重新索引数组