我们在各种项目中,尤其是框架源码中,经常可以看到self::
、static::
、parent::
等来调用静态变量、静态方法,本文就简单地说明这三种关键字有啥区别。
首先说明两个概念,转发调用
与非转发调用
- 转发调用:进行静态调用时,代码中不显式指明调用的对象,而是在运行时来判断调用哪个对象。比如使用关键字
self
、static
、parent
、forward_static_call
。
- 非转发调用:在进行方法、对象调用时,代码中显式指明调用的对象名称,如
A::getName()
或a->getName()
。
这三个关键字会进行转发调用,但他们转发的对象不同:
self::
转发给该语句定义所在的类
parent::
转发给该语句定义所在类的父类
static::
转发给调用该语句调用者所在类
听起来很晕,好在 PHP 也为我们提供了相应的函数、魔术变量来帮助获取相关信息,便于我们理解:
- 魔术变量
__CLASS__
可用来获取定义当前方法所在的类。
- 函数
get_called_class()
可用来获取调用者所在类。
先看如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
<?php
define('TEMPLATE', "%-12s %-5s %-12s %-12s\n");
class Base
{
protected static $value = 'Base';
}
class Car extends Base
{
protected static $value = 'Car';
public static function printSelf()
{
printf(TEMPLATE, __FUNCTION__, self::$value, get_called_class(), __CLASS__);
}
public static function printParent()
{
printf(TEMPLATE, __FUNCTION__, parent::$value, get_called_class(), __CLASS__);
}
public static function printStatic()
{
printf(TEMPLATE, __FUNCTION__, static::$value, get_called_class(), __CLASS__);
}
}
class Benz extends Car
{
protected static $value = 'Benz';
public static function printSelf()
{
printf(TEMPLATE, __FUNCTION__, self::$value, get_called_class(), __CLASS__);
}
public static function printParent()
{
printf(TEMPLATE, __FUNCTION__, parent::$value, get_called_class(), __CLASS__);
}
}
printf(TEMPLATE, 'function','value', 'called_class', 'defined_class');
Car::printSelf();
Car::printParent();
Car::printStatic();
Benz::printSelf();
Benz::printParent();
Benz::printStatic();
|
运行结果是:
1
2
3
4
5
6
7
|
function value called_class defined_class
printSelf Car Car Car
printParent Base Car Car
printStatic Car Car Car
printSelf Benz Benz Benz
printParent Car Benz Benz
printStatic Benz Benz Car
|
输出结果每一列分别为为函数名、获取到的 value 值、调用者所在类、被调用函数定义所在类.
除去标题,输出结果一共六行,编号为结果 1 ~ 结果 6。
由结果 1 与结果 4 ,Benz
与Car
类中均有printSelf()
方法,并都使用了self
关键字,因此他们各自会指向各自的定义函数所在类:Benz
与Car
。
由结果 2 与结果 5,Benz
与Car
类中均有printParent()
方法,并都使用了parent
关键字,因此他们各自会指向各自的定义函数所在类的父类:Car
与Base
。
由结果 3 与结果 6,Benz
类没有printStatic()
方法,虽然调用的是Car
类的方法,但调用方是Benz
类,故static::$value
输出为Benz
类的$value
;而Car
类调用printStatic()
方法,输出为Car
类的$value
总结一下,self::
与parent::
指向的是其定义所在函数的类(如 defined_class 列所示)的本身、的父类;而static::
指向的是其调用方的类(如 called_class 列所示)本身。
再提供几个例子,以供练习,加深理解。
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?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
。
说了这么多,那这三个关键字有什么用呢?
self
一般用来获取静态属性或调用静态方法。
parent
用来调用父类的方法或属性。
static
则用来调用调用者的方法或属性。比如,可以在Base
类中通过static
关键字调用子类中的方法,从而实现不用修改父类,只需修改子类,就能实现不同的功能,拓展性强。这种方法经常在框架中用到,Base 类写在框架中,而子类可由用户去实现,定制化强:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<?php
class Base
{
public static function work()
{
static::drive();
}
}
class Car extends Base
{
protected static function drive()
{
print("begin to drive car\n");
}
}
class Bicycle extends Base
{
protected static function drive()
{
print("begin to take bicycle\n");
}
}
Bicycle::work();
Car::work();
|
参考文献
PHP常见概念混淆(七)之self、static、parent的区别
PHP Manual:后期静态绑定
PHP中静态(static)调用非静态方法详解
PHP中静态(static)调用非静态方法详解