php5.3后静态绑定用法详解


Posted in PHP onNovember 11, 2016

本文实例讲述了php5.3后静态绑定用法。分享给大家供大家参考,具体如下:

手册原文:

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

准确说,后期静态绑定工作原理是存储了在上一个"非转发调用"(non-forwarding call)的类名。当进行静态方法调用时,该类名即为明确指定的那个(通常在 :: 运算符左侧部分);当进行非静态方法调用时,即为该对象所属的类。所谓的"转发调用"(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。可用 get_called_class() 函数来得到被调用的方法所在的类名,static:: 则指出了其范围

该功能从语言内部角度考虑被命名为"后期静态绑定"。"后期绑定"的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为"静态绑定",因为它可以用于(但不限于)静态方法的调用。

self:: 的限制

使用 self:: 或者 __CLASS__ 对当前类的静态引用,取决于定义当前方法所在的类:

Example #1 self:: 用法

<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>

以上例程会输出:

A

后期静态绑定的用法 后期静态绑定本想通过引入一个新的关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用 test() 时引用的类是 B 而不是 A。最终决定不引入新的关键字,而是使用已经预留的 static 关键字。

Example #2 static:: 简单用法

<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // 后期静态绑定从这里开始
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>

以上例程会输出:

B

Note: 在非静态环境下,所调用的类即为该对象实例所属的类。由于 $this-> 会在同一作用范围内尝试调用私有方法,而 static:: 则可能给出不同结果。另一个区别是 static:: 只能用于静态属性。

Example #3 非静态环境下使用 static::

<?php
class A {
private function foo() {
echo "success!\n";
}
public function test() {
$this->foo();
static::foo();
}
}
class B extends A {
/* foo() will be copied to B, hence its scope will still be A and
* the call be successful */
}
class C extends A {
private function foo() {
/* original method is replaced; the scope of the new one is C */
}
}
$b = new B();
$b->test();
$c = new C();
$c->test(); //fails
?>

以上例程会输出:

success!
success!
success!
Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9

Note: 后期静态绑定的解析会一直到取得一个完全解析了的静态调用为止。另一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

Example #4 转发和非转发调用

<?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();
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
?>

以上例程会输出:

A
C
C

下面示例分析了基于PHP后期静态绑定功能解决在继承范围内引用静态调用的类。

先看如下代码:

class Person
{
public static function status()
{
self::getStatus();
}
protected static function getStatus()
{
echo "Person is alive";
}
}
class Deceased extends Person
{
protected static function getStatus()
{
echo "Person is deceased";
}
}
Deceased::status(); //Person is alive

很明显,结果不是我们预期的,这是因为self::取决于定义时所在的类,而不是运行中的类。为了解决这个问题,你可能会在继承类中重写status()方法,更好的解决方案是PHP 5.3后添加了后期静态绑定的功能。

代码如下:

class Person
{
public static function status()
{
static::getStatus();
}
protected static function getStatus()
{
echo "Person is alive";
}
}
class Deceased extends Person
{
protected static function getStatus()
{
echo "Person is deceased";
}
}
Deceased::status(); //Person is deceased

可见,static::不在指向当前所在的类,实际上,它是在运行中计算的,强制获取最终类的所有属性。

因此,建议,以后不要再使用self::,使用static::

补充:

网友帖1

php的后期静态绑定,怎么解释?下面的这幅图输出是A,C,C

php5.3后静态绑定用法详解

由图的继承关系可知:C彻底包含了B和A。

在看答案结果以前,他细观察发现,三个类里都有同一个名称who()方法。
系统会用最后一个优先级最高,进一步的说,你几乎没法通过C去调用A、B内的who(),只能重改方法,比如添加个getBWho(){echo B::who();}
然后通过C::getBWho();来调用B内的who();

下面来看运行结果:

test只在B中出现,所以结果必然是test()中运行的三个结果:

第一个:静态直接指名到姓的调用A内静态函数,这没有悬念,必然是A
第二个:parent::是调用上一级的父类,在此题中为A,A中又直接调用static:who();上面说过了,这个who()优先级最高的在C里面,无论在你ABC中哪里调用,只要是static::who()必然是最后定义的那个,覆盖效应,如果想调用A里的必需指明A::who()或是通过去除static从作用域限制来实现。所以这个who()就是C中定义的who
第三个:self::who与第二个类似的问题,看样该走B的,注意覆盖效应,要想调用B内的who必须得B::who(),因为更高级的C已经重写了这个方法,如果C中没有who,肯定就是B,依次类推。所以必然还是调用C中的who;

所以答案为:ACC

代码如下:

<?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();
    self::foo();
  }
  public static function who() {
    echo __CLASS__."\n";
  }
}
class C extends B {
  //public static function who() {
  //  echo __CLASS__."\n";
  //}
}
C::test();
?>

输出为:A B B

网友帖2

(还是针对上面图中的代码)

手册不是说得很清楚么

”后期绑定“的意思是说,static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定“,因为它可以用于(但不限于)静态方法的调用。

#1说的有个小问题

【self::foo(); // 这个self实际上是C类。明白吗? C::test() C继承了B的test()方法】

不准确,self还是B类,但是本身没有覆写foo方法,所以就调用父类A的foo方法。

如果self实际是C类,那你试下self::foo();改成self::who();,应当打印C,但是打印B,这也正是self和static的区别。

<?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();
self::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
?>

输出为:A C B

网友帖3

A::foo(); //A指代A类,访问A类的foo方法和who方法
parent::foo();//调用B类的父类——A的foo方法,并告诉foo方法最原始的调用者是C
self::foo(); //self指代定义该方法的类,即B,但是B没有定义foo方法,它将原始的调用者C向上传递,
// 访问父类的foo方法,最后访问c的who方法;

所以这就回答了楼上的疑问:若是把self::foo(); 改成self::who(),因为self指代B,而B有who方法,所以结果是变成了B

静态调用使用 parent:: 或者 self:: 将转发原始调用信息。

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
优化NFR之一 --MSSQL Hello Buffer Overflow
Oct 09 PHP
兼容性最强的PHP生成缩略图的函数代码(修改版)
Jan 18 PHP
PHP 无限分类三种方式 非函数的递归调用!
Aug 26 PHP
浅析PHP中strlen和mb_strlen的区别
Aug 31 PHP
PHP里8个鲜为人知的安全函数分析
Dec 09 PHP
php实现的任意进制互转类分享
Jul 07 PHP
CI配置多数据库访问的方法
Mar 28 PHP
Laravel 5.1 on SAE环境开发教程【附项目demo源码】
Oct 09 PHP
使用php完成常见的文件上传功能(推荐)
Jan 13 PHP
PHP两种实现无级递归分类的方法
Mar 02 PHP
php curl上传、下载、https登陆实现代码
Jul 23 PHP
PHP实现Snowflake生成分布式唯一ID的方法示例
Aug 30 PHP
php基于curl实现的股票信息查询类实例
Nov 11 #PHP
PHP中STDCLASS用法实例分析
Nov 11 #PHP
php遍历替换目录下文件指定内容的方法
Nov 10 #PHP
php实现有序数组打印或排序的方法【附Python、C及Go语言实现代码】
Nov 10 #PHP
PHP数组生成XML格式数据的封装类实例
Nov 10 #PHP
Linux平台php命令行程序处理管道数据的方法
Nov 10 #PHP
PHP中功能强大却很少使用的函数实例小结
Nov 10 #PHP
You might like
PHP通用分页类page.php[仿google分页]
2008/08/31 PHP
PHPMailer安装方法及简单实例
2008/11/25 PHP
让Nginx支持ThinkPHP的URL重写和PATHINFO的方法分享
2011/08/08 PHP
php实现分页工具类分享
2014/01/09 PHP
体育彩票排列三组选三算法分享
2014/03/07 PHP
php常用hash加密函数
2014/11/22 PHP
php更新mysql后获取改变行数的方法
2014/12/25 PHP
PHP中cookie知识点学习
2018/05/06 PHP
PHP实现从PostgreSQL数据库检索数据分页显示及根据条件查找数据示例
2018/06/09 PHP
JQuery UI皮肤定制
2009/07/27 Javascript
关于onchange事件在IE和FF下的表现及解决方法
2014/03/08 Javascript
nodejs npm package.json中文文档
2014/09/04 NodeJs
JavaScript中浅讲ajax图文详解
2016/11/11 Javascript
Bootstrap面板学习使用
2017/02/09 Javascript
Vue2单一事件管理组件通信
2017/05/09 Javascript
vue使用element-ui的el-input监听不了回车事件的解决方法
2018/01/12 Javascript
基于vue-cli vue-router搭建底部导航栏移动前端项目
2018/02/28 Javascript
Vue组件教程之Toast(Vue.extend 方式)详解
2019/01/27 Javascript
js实现页面图片消除效果
2020/03/24 Javascript
JS使用Chrome浏览器实现调试线上代码
2020/07/23 Javascript
python判断字符串是否纯数字的方法
2014/11/19 Python
Python正则替换字符串函数re.sub用法示例
2017/01/19 Python
python 读取excel文件生成sql文件实例详解
2017/05/12 Python
python实现K最近邻算法
2018/01/29 Python
python ipset管理 增删白名单的方法
2019/01/14 Python
十分钟搞定pandas(入门教程)
2019/06/21 Python
python+adb+monkey实现Rom稳定性测试详解
2020/04/23 Python
用gpu训练好的神经网络,用tensorflow-cpu跑出错的原因及解决方案
2021/03/03 Python
html5 canvas-2.用canvas制作一个猜字母的小游戏
2013/01/07 HTML / CSS
HTML5 Canvas如何实现纹理填充与描边(Fill And Stroke)
2013/07/15 HTML / CSS
Homestay中文官网:全球寄宿家庭
2018/10/18 全球购物
机械化及自动化毕业生的自我评价分享
2013/11/06 职场文书
法人任命书范本
2014/06/04 职场文书
高中物理教学反思
2016/02/19 职场文书
如何用Laravel包含你自己的帮助函数
2021/05/27 PHP
什么是动态刷新率DRR? Windows11动态刷新率功能介绍
2021/11/21 数码科技