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时出现的单例继承问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows电脑SMB共享设置方法

SMB简介&#xff1a; SMB&#xff08;Server Message Block&#xff09;(*nix平台和Win NT4.0又称CIFS)协议是Windows平台标准文件共享协议&#xff0c;Linux平台通过samba来支持。SMB最新版本v3.0&#xff0c;在v2.0基础上针对WAN和分布式有改进。 建议使用原版wind…

php个人扫码支付,PHP个人发卡网源码,支持MA支付对接,扫码自动发货

PHP个人发卡网源码&#xff0c;支持MA支付对接&#xff0c;扫码自动发货。源码介绍个人发卡网源码&#xff0c;支持码支付对接&#xff0c;扫码自动发货.自适应网页&#xff0c;可为商品设置优惠套餐&#xff0c;后台管理功能丰富。前台发卡页面有点粗糙&#xff0c;已开源的可…

NtLmSsp

NTLMSSP (NT LAN Manager Security Support Provider)&#xff0c;是微软提供的安全支持接口协议&#xff0c;常用于SMB共享。估计你是从linux的fstab配置文件看来的选项吧。这个是参数指定了linux挂载smb网络共享时使用的加密方法。 NtLmSsp&#xff08;NT LM安全性支持提供者…

php让代码重新运行一次,脚本运行时是否可以动态重新加载PHP代码?

我有一个使用PHPSockets的多人游戏服务器,因此完全用PHP编写.目前,每当我对PHP服务器脚本进行任何更改时,我都必须终止该脚本,然后重新开始.这意味着任何在线用户都会断开连接(通常不会出现问题,因为目前没有这么多用户).现在我正在重写服务器脚本以使用自定义PHP类并对其进行一…

php网页表格样式,HTML5制作表格样式

废话不多说了&#xff0c;具体代码如下所示&#xff1a;表格*{margin: 0;padding: 0;}body{font: italic 20px Georgia, serif;letter-spacing: normal;background-color: #f0f0f0;}#content{width: 750px;padding: 40px;margin: 0 auto;background-color: #fff;border-left: 3…

SharePoint 开发TimerJob 介绍

项目需要写TimerJob&#xff0c;以前也大概知道原理&#xff0c;不过&#xff0c;开发过程中&#xff0c;还是遇到一些问题&#xff0c;网上看了好多博客&#xff0c;也有写的灰常好的&#xff0c;不过&#xff0c;自己还是想再写一下&#xff0c;也算是给自己一个总结&#xf…

【转】如何写出让同事无法维护的代码

译者&#xff1a;陈皓 &#xff08;左耳朵耗子&#xff09; 译文&#xff1a;http://coolshell.cn/articles/4758.html 对&#xff0c;你没看错&#xff0c;本文就是教你怎么写出让同事无法维护的代码。 一、程序命名 容易输入的变量名。比如&#xff1a;Fred&#xff0c;as…

linux php环境搭建 图文教程,linux php环境搭建教程

1) 安装依赖包yum -y install wget vim pcre pcre-devel openssl openssl-devel \libicu-devel gcc gcc-c autoconf libjpeg libjpeg-devel libpng \libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib \zlib-devel glibc glibc-devel glib2 glib2-devel ncurse…

【转】The password supplied with the username Domain\UserName was not correct. AD密码定期更换

起因 今天想进入SharePoint 2013 Central Administration创建一个WebApplication&#xff0c;尽然发生了错误&#xff1a; The password supplied with the username Domain\UserName was not correct. Verify that it was entered correctly and try again 具体如下图&#x…

XHR简介

在XHR诞生前&#xff0c;网页要获取客户端和服务器的任何状态更新&#xff0c;都需要刷新一次&#xff0c;在XHR诞生后就可以完全通过JS代码异步实现这一过程。XHR的诞生也使最初的网页制作转换为开发交互应用&#xff0c;拉开了WEB2.0的序幕。 XHR是一种浏览器API&#xff0…

织梦php echo 调用金币,dedecms实现任意页面调用当前会员信息的方法

文实例讲述了dedecms实现任意页面调用当前会员信息的方法。分享给大家供大家参考。具体实现方法如下&#xff1a;1、在你需要调用的页面里面之间加入代码如下:function CheckInfo(){var taget_obj document.getElementByIdx_x(_userinfo);myajax new DedeAjax(taget_obj,fals…

第一节: Timer的定时任务的复习、Quartz.Net的入门使用、Aop思想的体现

一. 前奏-Timer类实现定时任务 在没有引入第三方开源的定时调度框架之前&#xff0c;我们处理一些简单的定时任务同时都是使用Timer类&#xff0c; DotNet中的Timer类有三个&#xff0c;分别位于不同的命名空间下&#xff0c;分别是&#xff1a; ①.位于System.Windows.Forms里…

php 自动验证类,Thinkphp实现自动验证和自动完成

这篇文章主要介绍了Thinkphp实现自动验证和自动完成的相关资料,需要的朋友可以参考下Thinkphp的自动验证和自动完成都是根所表单提交的内容来的&#xff0c;对部分数据进行规则验证和处理后插入到数据库。1、自动验证格式&#xff1a;array(array(验证字段1,验证规则,错误提示,…

第二节:比较DateTime和DateTimeOffset两种时间类型并介绍Quartz.Net中用到的几类时间形式(定点、四舍五入、倍数、递增)

一. 时间的类型 1. 背景 这里为什么要介绍时间类型呢&#xff0c;明明是定时调度篇&#xff0c;原因是在定时任务中&#xff0c;任务什么时间开始执行&#xff0c;什么时间结束执行&#xff0c;要用到各种各样的时间模式&#xff0c;虽然这不能算是一个复杂的问题&#xff0c;但…

PHP中英文截取函数,php字符串截取函数,支持中英文混体

php字符串截取函数,支持中英文混体,以前我们截取字符串都会用php自带的函数,今天我来看一下一款字符串截取函数,支持中英文混体的php代码实例吧.php字符串截取函数代码如下:function cutstr($string, $sublen10, $start 0, $code utf-8){if($code utf-8){$pa "/[x01-x…

第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联等)

一. 五大构件 引言&#xff1a; Quartz.Net的五大构件 1. 调度器&#xff1a;Scheduler 2. 作业任务&#xff1a;Job 3. 触发器&#xff1a; Trigger 4. 线程池&#xff1a; SimpleThreadPool 5. 作业持久化&#xff1a;JobStore 二. Scheduler详解 1. 创建Scheduler的两…

创建phpinfo.php,如何创建phpinfo查看php信息

用户提问如何创建phpinfo查看php信息&#xff1f;To create the file simply add the following lines of code to a blank document and name the file phpinfo.php 创建一个简单的文本文档并命名为phpinfo.php<?phpphpinfo ();?>Save the file as phpinfo.php and pl…

第四节: Quartz.Net五大构件之Trigger通用用法(常用方法、优先级、与job关联等)

一. 简介 1. 几个类型&#xff1a; ①&#xff1a;TriggerBuilder&#xff1a;用来创建ITrigger实例 ②&#xff1a;ITrigger&#xff1a;触发器实例 2.常用的几个方法 ①.StartNow&#xff1a;Trigger马上触发. ②.StartAt和EndAt&#xff1a;设置Trigger触发的开始时间和结…

php键值对数组排序,PHP按指定键值对二维数组进行排序的方法_PHP

本文实例讲述了PHP按指定键值对二维数组进行排序的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;问题&#xff1a;有数组&#xff1a;代码如下:array(0>array(id>1,price>50),1>array(id>2,price>60));要求根据数组的price这个字段进行排序。…

oracle 10g中没有refhost.xml,解决win7 安装oracle10g的问题

当你使用win7安装oracle10g&#xff0c;很可能显示相关oraparam.ini相关的异常&#xff0c;导致无法继续安装oracle10g&#xff0c;可以通过以下步骤方式来安装&#xff0c;至于安装oracle后的稳定性&#xff0c;目前无法认证&#xff0c;只有发现问题后&#xff0c;才可下结论…