文档章节

PHP 中 parent、self、static、$this 的区别 & 后期静态绑定详解

echojson
 echojson
发布于 04/01 23:46
字数 2227
阅读 83
收藏 1
PHP

自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。 虽然也可以调用非静态方法,但是不会在运行时绑定。

static 不再只是简单的静态修饰关键字。而是还可以调用类的静态方法,非静态方法,为什么静态非静态要分开说呢,因为调用的效果是不一样的。

实例

class  A  {
	
	protected $name = 'A';
	static $alias = 'a';
	const HASH = 'md5';

	public function dd() {
		echo 'name:' . $this->name . PHP_EOL;
		echo 'self-alias:' . self::$alias . PHP_EOL;
		echo 'static-alias:' . static::$alias . PHP_EOL;  // 后期静态绑定
		echo 'self-hash:' . self::HASH . PHP_EOL;
		echo 'static-hash:' . static::HASH . PHP_EOL;  // 后期静态绑定

		var_dump(new self);
		var_dump($this);
		var_dump(new static);

	}


    public static function  who () {
        echo  __CLASS__ ; 
        echo ' [ This is A ]';
        echo PHP_EOL;
    }

    public static function  test () {
        static:: who ();  // 后期静态绑定从这里开始
    }

    public static function  test2 () {
        self:: who ();
    }
}
 
class  B  extends  A  {

	protected $name = 'B';
	static $alias = 'b';
	const HASH = 'sha1';

    public static function  who () {
        echo  __CLASS__ ; 
        echo ' [ This is B ]';
        echo PHP_EOL;
    }
}



(new B)->dd()

结果输出:

name:B
self-alias:a
static-alias:b
self-hash:md5
static-hash:sha1

object(admin\controllers\A)
  protected 'name' => string 'A' (length=1)

object(admin\controllers\B)
  protected 'name' => string 'B' (length=1)

object(admin\controllers\B)
  protected 'name' => string 'B' (length=1)

执行:

B::who();
B::test();
B::test2();

输出:


B [ This is B ]
B [ This is B ]
A [ This is A ]

总结说明:

  • self__CLASS__,都是对当前类的静态引用,取决于定义当前方法所在的类。也就是说,self 写在哪个类里面, 它引用的就是谁。

  • $this 指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中。

  • static 关键字除了可以声明类的静态成员(属性和方法)外,还有一个非常重要的作用就是后期静态绑定。

  • parent,是对当前类的父类的静态引用。

  • self 可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制。

  • $this 指向的对象所属的类和 static 指向的类相同。

  • static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性。

  • 静态调用时,static 指向的是实际调用时的类;非静态调用时,static 指向的是实际调用时的对象所属的类。

后期静态绑定

后期静态绑定(也叫延迟静态绑定),可用于在继承范围内引用静态调用的类,也就是代码运行时最初调用的类。

工作原理

确切地说,static 后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。

当进行静态方法调用时,该类名(static指向的类名)为明确指定的那个(通常是 :: 运算符的左侧部分),即实际调用时的类。

如:上面例子中的

A::test();  //A::test() 调用的是 static::who(),这里static指向的便是A,所以执行的就是A::who();
B::test();  //A::test() 调用的是 static::who(),这里static指向的便是B,所以执行的就是B::who();

static指向的类名,指向的就是实际调用的类

对比

static 和 self 的区别:

  • self 可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制。

  • static 也可以用于访问类的静态属性、静态方法和常量,static 指向的是实际调用时的类。当进行非静态方法调用时,该类名(static指向的类名)为该对象所属的类,即实际调用时的对象所属的类。

static 和 $this 有点类似,但又有区别:

  • $this 指向的对象所属的类和 static 指向的类相同。
  • $this 不能用于静态方法中,也不能访问类的静态属性和常量。
  • $this 指向的是实际调用的对象。
  • static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性。
  • static 指向的是实际调用时的对象所属的类。

转发调用(forwarding call)

所谓的转发调用(forwarding call)指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call() 。

可用 get_called_class() 函数来获取被调用的方法所在的类名。

以下四种形式的调用,都是转发调用:

self::
parent::
static::
forward_static_call()

除此之外的调用,就是非转发调用。

非转发调用(non-forwarding call)

后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。

通过具体的类名或具体的对象进行的调用都是非转发调用。

注意事项

非静态环境下的私有方法的查找顺序

在非静态环境下,在类的非静态方法中,使用 $this 和 static 调用类的私有方法时,执行方式有所不同。

  • $this 会优先寻找所在定义范围(父类)中的私有方法,如果存在就调用。
  • static 是先到它指向的类(子类)中寻找私有方法,如果找到了就会报错,因为私有方法只能在它所定义的类内部调用;如果没找到,再去所在定义范围(父类)中寻找该私有方法,如果存在就调用。

具体来说,$this 会先到所在定义范围内寻找私有方法,再到它指向的对象所属的类中寻找私有方法,然后寻找公有方法,最后到所在定义范围内寻找公共方法。只要找到了匹配的方法,就调用,并停止查找。

而 static 则是先到它指向的类中寻找私有方法,再寻找共有方法;然后到所在定义范围内寻找私有方法,再寻找共有方法。只要找到了匹配的方法,就调用,并停止查找。

下面是一个例子:

<?php
 class  A  {
    private function  foo () {
        var_dump($this); echo '--';
        var_dump(new static); echo '--';
 
        echo __CLASS__; echo '--';
        echo get_called_class();
        echo '<br>';
    }
 
    public function  test () {
        $this -> foo ();
        static:: foo ();
        echo '<br>';
    }
}
 
class  B  extends  A  { }
 
class  C  extends  A  {
    private function foo () {
        echo 'this is C';
    }
}
 
(new  B())->test();
(new  C())->test();
输出结果为:

object(B)#1 (0) { } --object(B)#2 (0) { } --A--B
object(B)#1 (0) { } --object(B)#2 (0) { } --A--B
 
object(C)#1 (0) { } --object(C)#2 (0) { } --A--C
 
Fatal error: Uncaught Error: Call to private method C::foo() from context 'A'

关于后期静态绑定的解析

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

<?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 ();
        static::foo();
        forward_static_call(['A', 'foo']);
        echo '<br>';
    }
 
    public static function  who () {
        echo  __CLASS__ . "\n" ;
    }
}
 
class  C  extends  B  {
    public static function  who () {
        echo  __CLASS__ . "\n" ;
    }
 
    public static function test2() {
        self::test();
    }
}
 
class  D  extends  C  {
    public static function  who () {
        echo  __CLASS__ . "\n" ;
    }
}
 
B::foo();
B::test();
 
C::foo();
C::test();
 
D::foo();
D::test2();

以上的输出结果为:

B A B B B B 
C A C C C C 
D A D D D D

static 后期静态绑定的工作原理是存储了上一个非转发调用(non-forwarding call)的类名。请记住这句话。

下面的例子是非转发调用。

A::foo();  // 输出 A
 
B::foo();   // 输出 B
 
C::foo();   // 输出 C

后期静态绑定 static ,是定义在了 foo() 方法中,哪个类通过非转发调用的形式调用 foo() 方法, foo() 方法中的 static 指向的就是哪个类。

但是,如果通过转发调用的形式,调用 foo() 方法,如:

parent :: foo ();
self :: foo ();
static::foo();
forward_static_call(['A', 'foo']);

那么,就以转发调用代码所在的方法 test() 为准,哪个类通过非转发调用的形式调用 test() 方法, foo() 方法中的 static 指向的就是哪个类。

假如调用 test() 方法时,也采用了转发调用的形式,如:

public static function test2() {
    self::test();
}

那么,就以 test2() 方法为准 ... 依次类推。

也就是说,在使用了后期静态绑定的基类中,后期静态绑定所在的方法如果被转发调用,则 static 的指向,会一直向上追溯,直到遇到非转发调用的形式。

© 著作权归作者所有

echojson
粉丝 3
博文 162
码字总数 245732
作品 0
深圳
技术主管
私信 提问
__CLASS__ get_class() get_called_class()区别

CLASS获取当前的类名, get_class()与上面一样,都是获取当前的类名 getcalledclass()后期静态绑定类的名称 class testA { function printS() { echo 'testA' . CLASS . '<br/>'; echo 'test......

geek土拨鼠
02/14
8
0
复习PHP-语言参考-类与对象

1.简介 没啥说的。 2.基本概念 类有自己的属性和方法,内部使用可用伪变量$this(是该类的一个引用)访问属性或方法 类通过new被实例化(如果该类属于某个命名空间,则需要写全) 可以通过c...

qzd1989
2014/04/30
0
0
通过工厂模式来认识PHP的静态延迟绑定

通过简单的工厂模式来认识PHP的静态延迟绑定 当我们需要为很多类添加实例化自己的成员函数时,可以用工厂设计模式 + 静态延迟绑定的特性,在定级类中创建实例化上下文类的方法,继而让子类可...

big_cat
2015/09/22
101
0
PHP中this,self,parent的区别

{一}PHP中this,self,parent的区别之一this篇 面向对象编程(OOP,Object OrientedProgramming)现已经成为编程人员的一项基本技能。利用OOP的思想进行PHP的高级编程,对于提高PHP编程能力和规划...

一切_放空
2013/08/27
192
0
PHP中this,self,parent的区别

{一}PHP中this,self,parent的区别之一this篇 面向对象编程(OOP,Object OrientedProgramming)现已经成为编程人员的一项基本技能。利用OOP的思想进行PHP的高级编程,对于提高PHP编程能力和规划...

随智阔
2012/11/21
70
0

没有更多内容

加载失败,请刷新页面

加载更多

【0918】正则介绍_grep

【0918】正则介绍_grep 9.1 正则介绍_grep上 9.2 grep中 9.3 grep下 一、正则介绍 正则是一串有规律的字符串,它使用单个字符串来描述或匹配一系列符合某个语法规则的字符串。 二、grep工具 ...

飞翔的竹蜻蜓
33分钟前
4
0
为什么要在网站中应用CDN加速?

1. 网页加载速度更快 在网站中使用CDN技术最直接的一个好处就是它可以加快网页的加载速度。首先,CDN加速的内容分发是基于服务器缓存的,由于CDN中缓存了不少数据,它能够给用户提供更快的页...

云漫网络Ruan
今天
8
0
亚玛芬体育(Amer Sports)和信必优正式启动合作开发Movesense创新

亚玛芬体育和信必优正式启动合作开发Movesense创新,作为亚玛芬体育的完美技术搭档,信必优利用Movesense传感器技术为第三方开发移动应用和服务。 Movesense基于传感器技术和开放的API,测量...

symbiochina88
今天
4
0
创龙TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA核心板规格书

SOM-TL437xF是一款广州创龙基于TI AM437x ARM Cortex-A9 + Xilinx Spartan-6 FPGA芯片设计的核心板,采用沉金无铅工艺的10层板设计,适用于高速数据采集和处理系统、汽车导航、工业自动化等领...

Tronlong创龙
今天
5
0
好程序员Java学习路线分享MyBatis之线程优化

  好程序员Java学习路线分享MyBatis之线程优化,我们的项目存在大量用户同时访问的情况,那么就会出现大量线程并发访问数据库,这样会带来线程同步问题,本章我们将讨论MyBatis的线程同步问...

好程序员官方
今天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部