php后静态绑定,详解PHP后期静态绑定分析与应用
基础知识
1. 范围解析操作符 (::)
可以用于访问静态成员,类常量,还可以用于覆盖类中的属性和方法。
self,parent 和 static 这三个特殊的关键字是用于在类定义的内部对其属性或方法进行访问的。
parent用于调用父类中被覆盖的属性或方法(出现在哪里,就将解析为相应类的父类)。
self用于调用本类中的方法或属性(出现在哪里,就将解析为相应的类;注意与$this区别,$this指向当前实例化的对象)。
当一个子类覆盖其父类中的方法时,PHP 不会调用父类中已被覆盖的方法。是否调用父类的方法取决于子类。
2. PHP内核将类的继承实现放在了"编译阶段"
class A{
const H = 'A';
const J = 'A';
static function testSelf(){
echo self::H; //在编译阶段就确定了 self解析为 A
}
}
class B extends A{
const H = "B";
const J = 'B';
static function testParent(){
echo parent::J; //在编译阶段就确定了 parent解析为A
}
/* 若重写testSelf则能输出“B”, 且C::testSelf()也是输出“B”
static function testSelf(){
echo self::H;
}
*/
}
class C extends B{
const H = "C";
const J = 'C';
}
B::testParent();
B::testSelf();
echo "\n";
C::testParent();
C::testSelf();
运行结果:
AA
AA
结论:
self::和parent::出现在某个类X的定义中,则将被解析为相应的类X,除非在子类中覆盖父类的方法。
3.Static(静态)关键字
作用:
- 在函数体内的修饰变量的static关键字用于定义静态局部变量。
- 用于修饰类成员函数和成员变量时用于声明静态成员。
- (PHP5.3之后)在作用域解析符(::)前又表示静态延迟绑定的特殊类。
例子:
定义静态局部变量(出现位置:局部函数中)
特征:静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
定义静态方法,静态属性
a)声明类属性或方法为静态,就可以不实例化类而直接访问。
b)静态属性不能通过一个类已实例化的对象来访问(但静态方法可以)
c)如果没有指定访问控制,属性和方法默认为公有。
d)由于静态方法不需要通过对象即可调用,所以伪变量 $this 在静态方法中不可用。
e)静态属性不可以由对象通过 -> 操作符来访问。
f)用静态方式调用一个非静态方法会导致一个 E_STRICT 级别的错误。
g)就像其它所有的 PHP 静态变量一样,静态属性只能被初始化为文字或常量,不能使用表达式。所以可以把静态属性初始化为整数或数组,但不能初始化为另一个变量或函数返回值,也不能指向一个对象。
a.静态方法例子(出现位置: 类的方法定义)
class Foo {
public static function aStaticMethod() {
// ...
}
}
Foo::aStaticMethod();
$classname = 'Foo';
$classname::aStaticMethod(); // 自PHP 5.3.0后,可以通过变量引用类
?>
b.静态属性例子(出现位置:类的属性定义)
class Foo
{
public static $my_static = 'foo';
public function staticValue() {
return self::$my_static; //self 即 FOO类
}
}
class Bar extends Foo
{
public function fooStatic() {
return parent::$my_static; //parent 即 FOO类
}
}
print Foo::$my_static . "\n";
$foo = new Foo();
print $foo->staticValue() . "\n";
print $foo->my_static . "\n"; // Undefined "Property" my_static
print $foo::$my_static . "\n";
$classname = 'Foo';
print $classname::$my_static . "\n"; // As of PHP 5.3.0
print Bar::$my_static . "\n";
$bar = new Bar();
print $bar->fooStatic() . "\n";
?>
c.用于后期静态绑定(出现位置: 类的方法中,用于修饰变量或方法)
下面详细分析
后期静态绑定(late static binding)
自 PHP 5.3.0 起,PHP 增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。
1.转发调用与非转发调用
转发调用 :
指的是通过以下几种方式进行的静态调用:self::,parent::,static:: 以及 forward_static_call()。
非转发调用 :
明确指定类名的静态调用(例如Foo::foo())
非静态调用(例如$foo->foo())
2.后期静态绑定工作原理
原理:存储了在上一个“非转发调用”(non-forwarding call)中的类名。意思是当我们调用一个转发调用的静态调用时,实际调用的类是上一个非转发调用的类。
例子分析:
class A {
public static function foo() {
echo __CLASS__."\n";
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
echo "A::foo()\n";
A::foo();
echo "parent::foo()\n";
parent::foo();
echo "self::foo()\n";
self::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
class C extends B {
public static function who() {
echo __CLASS__."\n";
}
}
C::test();
/*
* C::test(); //非转发调用 ,进入test()调用后,“上一次非转发调用”存储的类名为C
*
* //当前的“上一次非转发调用”存储的类名为C
* public static function test() {
* A::foo(); //非转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为A,然后实际执行代码A::foo(), 转 0-0
* parent::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处的parent解析为A ,转1-0
* self::foo(); //转发调用, 进入foo()调用后,“上一次非转发调用”存储的类名为C, 此处self解析为B, 转2-0
* }
*
*
* 0-0
* //当前的“上一次非转发调用”存储的类名为A
* public static function foo() {
* static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为A, 故实际执行代码A::who(),即static代表A,进入who()调用后,“上一次非转发调用”存储的类名依然为A,因此打印 “A”
* }
*
* 1-0
* //当前的“上一次非转发调用”存储的类名为C
* public static function foo() {
* static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”
* }
*
* 2-0
* //当前的“上一次非转发调用”存储的类名为C
* public static function foo() {
* static::who(); //转发调用, 因为当前的“上一次非转发调用”存储的类名为C, 故实际执行代码C::who(),即static代表C,进入who()调用后,“上一次非转发调用”存储的类名依然为C,因此打印 “C”
* }
*/
故最终结果为:
A::foo()
A
A
parent::foo()
A
C
self::foo()
A
C
3.更多静态后期静态绑定的例子
a)Self, Parent 和 Static的对比
class Mango {
function classname(){
return __CLASS__;
}
function selfname(){
return self::classname();
}
function staticname(){
return static::classname();
}
}
class Orange extends Mango {
function parentname(){
return parent::classname();
}
function classname(){
return __CLASS__;
}
}
class Apple extends Orange {
function parentname(){
return parent::classname();
}
function classname(){
return __CLASS__;
}
}
$apple = new Apple();
echo $apple->selfname() . "\n";
echo $apple->parentname() . "\n";
echo $apple->staticname();
?>
运行结果:
Mango
Orange
Apple
b)使用forward_static_call()
class Mango
{
const NAME = 'Mango is';
public static function fruit() {
$args = func_get_args();
echo static::NAME, " " . join(' ', $args) . "\n";
}
}
class Orange extends Mango
{
const NAME = 'Orange is';
public static function fruit() {
echo self::NAME, "\n";
forward_static_call(array('Mango', 'fruit'), 'my', 'favorite', 'fruit');
forward_static_call('fruit', 'my', 'father\'s', 'favorite', 'fruit');
}
}
Orange::fruit('NO');
function fruit() {
$args = func_get_args();
echo "Apple is " . join(' ', $args). "\n";
}
?>
运行结果:
Orange is
Orange is my favorite fruit
Apple is my father's favorite fruit
c)使用get_called_class()
class Mango {
static public function fruit() {
echo get_called_class() . "\n";
}
}
class Orange extends Mango {
//
}
Mango::fruit();
Orange::fruit();
?>
运行结果:
Mango
Orange
应用
前面已经提到过了,引入后期静态绑定的目的是:用于在继承范围内引用静态调用的类。
所以, 可以用后期静态绑定的办法解决单例继承问题。
先看一下使用self是一个什么样的情况:
// new self 得到的单例都为A。
class A
{
protected static $_instance = null;
protected function __construct()
{
//disallow new instance
}
protected function __clone(){
//disallow clone
}
static public function getInstance()
{
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
}
class B extends A
{
protected static $_instance = null;
}
class C extends A{
protected static $_instance = null;
}
$a = A::getInstance();
$b = B::getInstance();
$c = C::getInstance();
var_dump($a);
var_dump($b);
var_dump($c);
运行结果:
E:\code\php_test\apply\self.php:37:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:38:
class A#1 (0) {
}
E:\code\php_test\apply\self.php:39:
class A#1 (0) {
}
通过上面的例子可以看到,使用self,实例化得到的都是类A的同一个对象
再来看看使用static会得到什么样的结果
// new static 得到的单例分别为D,E和F。
class D
{
protected static $_instance = null;
protected function __construct(){}
protected function __clone()
{
//disallow clone
}
static public function getInstance()
{
if (static::$_instance === null) {
static::$_instance = new static();
}
return static::$_instance;
}
}
class E extends D
{
protected static $_instance = null;
}
class F extends D{
protected static $_instance = null;
}
$d = D::getInstance();
$e = E::getInstance();
$f = F::getInstance();
var_dump($d);
var_dump($e);
var_dump($f);
运行结果:
E:\code\php_test\apply\static.php:35:
class D#1 (0) {
}
E:\code\php_test\apply\static.php:36:
class E#2 (0) {
}
E:\code\php_test\apply\static.php:37:
class F#3 (0) {
}
可以看到,使用static可以解决self时出现的单例继承问题。
php后静态绑定,详解PHP后期静态绑定分析与应用相关推荐
- php self this static,PHP 中 self、static、$this 的区别和后期静态绑定详解
本篇文章给大家分享的内容是关于PHP 中 self.static.$this 的区别和后期静态绑定详解,有着一定的参考价值,有需要的朋友可以参考一下 self.static 和 $this 的区别 为 ...
- 动态绑定和静态绑定详解
动态绑定和静态绑定详解 弄清调用对象方法的执行过程十分重要.下面是调用过程的详细描述: 1.编译器查看对象的声明类型(注意和实际类型区分)和方法名.假设调用x.f(param),且隐式参数x声明为C类 ...
- hadoop作业初始化过程详解(源码分析第三篇)
(一)概述 我们在上一篇blog已经详细的分析了一个作业从用户输入提交命令到到达JobTracker之前的各个过程.在作业到达JobTracker之后初始化之前,JobTracker会通过submit ...
- spark RDD详解及源码分析
spark RDD详解及源码分析 @(SPARK)[spark] spark RDD详解及源码分析 一基础 一什么是RDD 二RDD的适用范围 三一些特性 四RDD的创建 1由一个已经存在的scala ...
- spark 调度模块详解及源码分析
spark 调度模块详解及源码分析 @(SPARK)[spark] spark 调度模块详解及源码分析 一概述 一三个主要的类 1class DAGScheduler 2trait TaskSched ...
- FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析
FPGA学习之路--I2C协议详解+Verilog源码分析 定义 I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时 ...
- Batch Normalization详解(原理+实验分析)
Batch Normalization详解(原理+实验分析) 1. 计算过程 2. 前向传播过程 3. 反向传播过程 4. 实验分析 4.1 实验一:验证有没有BatchNorm下准确率的区别 4.2 ...
- 方案详解|AARRR+八角行为分析=用游戏化思维实现用户增长
我们需要一套基于AARRR模型,围绕增长成本.效率.质量三个话题来针对每一层转化漏斗提炼可操作的运营方案,以AARRR模型+八角行为分析法为理论框架的,游戏化运营增长策略应运而生.随着互联网线上流量的 ...
- Linux系统调用详解(实现机制分析)
为什么需要系统调用 linux内核中设置了一组用于实现系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户 ...
最新文章
- 为什么大多数IP地址通常以192.168开头?
- web项目html页面过多,详解webpack4多入口、多页面项目构建案例
- PingingLab传世经典系列《CCNA完全配置宝典》-5.8 静态NAT
- ​rsync生产排错FAQ整理16
- 线性序列机与串行接口ADC驱动设计与验证
- C++设计模式-中介者模式
- VC++中实现INI文件读写的方法和示例
- 记一次 React 组件无法更新状态值的问题分析与解决
- 母亲节:微信喊你给母亲充钱 华为帮你教爸妈用手机
- 你们知道内卷化最严重的地方是哪里吗?
- screen 命令使用及示例
- ICPC程序设计题解系列
- 【RobotStudio学习笔记】(七)工件坐标
- iOS开发UI篇—简单的浏览器查看程序
- 【HMS core】【Analytics Kit】华为分析服务常见问题FAQ 2
- SAP系统Sizing的原理
- 你不知道的浏览器页面渲染机制
- mysql按时间查询的优化_mysql按时间查询优化的方法
- 公考复盘(一)——第三季第四季
- unity Input
热门文章
- bash中将字符串split成数组的方法
- [Leetcode][第216题][JAVA][数组之和3][回溯]
- 算术类型转换、整型提升
- 计算机科学与导论期末论文,计算机科学与导论论文3
- java数字不等于_java – 仅使用set中的数字查找等于或大于给定目标的总和
- jquery查找ul属性不是hide,jQuery的ul显示/隐藏功能
- python如何输入空行_在python中,如何在接受用户输入时跳过空行?
- 车间生产能耗管控方案_如何给生产车间降温 环保空调的这些方案一定能帮到你...
- 1359C. Mixing Water
- java peek函数_基础篇:JAVA.Stream函数,优雅的数据流操作