php中__destruct与register_shutdown_function执行的先后顺序问题


Posted in PHP onOctober 17, 2014

根据php手册的解析。

__destruct是

析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行。

register_shutdown_function

Registers a callback to be executed after script execution finishes or exit() is called. 注册一个回调函数,此函数在脚本运行完毕或调用exit()时执行。

从字面上理解,__destruct是对象层面的,而register_shutdown_function是整个脚本层面的,理应register_shutdown_function的级别更高,其所注册的函数也应最后执行。为证实我们的猜测,我们写一段脚本:

register_shutdown_function(function(){echo 'global';});

    class A {

        public function __construct(){

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    new A;

执行结果:

A::__destruct 

global

完全证实了我们的猜测,它按照对象->脚本的顺序被执行了。

但如果我们在对象中注册了register_shutdown_function呢?它还是一样的顺序吗?!

class A {

        public function __construct(){

            register_shutdown_function(function(){echo 'local', '<br/>';});

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    new A;

结果:

local 

A::__destruct

可以看到register_shutdown_function先被调用了,最后才是执行对象的__destruct。这表明register_shutdown_function注册的函数被当作类中的一个方法?!不得而知,这可能需要查看php源代码才能解析了。

我们可以扩大范围查看情况:

register_shutdown_function(function(){echo 'global', '<br/>';});

    class A {

        public function __construct(){

            register_shutdown_function(array($this, 'op'));

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

        public function op()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    class B {

        public function __construct()

        {

            register_shutdown_function(array($this, 'op'));

            $obj = new A;

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

        public function op()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    $b = new B;

我们在全局注册一个register_shutdown_function函数,在类AB中又各注册了一个,而且类中分别还有析构方法。最后运行结果会怎样呢?

global 

B::op 

A::op 

A::__destruct 

B::__destruct

结果完全颠覆了我们的想像,register_shutdown_function函数无论在类中注册还是在全局注册,它都是先被执行,类中执行的顺序就是它们被注册的先后顺序。如果我们再仔细研究,全局的register_shutdown_function函数无论放在前面还是后面都是这个结果,事情似乎有了结果,那就是register_shutdown_function比__destruct先执行,全局的register_shutdown_function函数又先于类中注册的register_shutdown_function先执行。

且慢,我无法接受这个结果,按照这样的结论,难道说脚本已经结束后还可以再执行__destruct?!因此,我还要继续验证这个结论---去掉类中注册register_shutdown_function,而保留全局register_shutdown_function:

class A {

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    class B {

        public function __construct()

        {

            $obj = new A;

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    register_shutdown_function(function(){echo 'global', '<br/>';});

输出:

A::__destruct 

global 

B::__destruct

结果令人茫然,A、B两个类的析构函数执行顺序无可质疑,因为B中调用了A,类A肯定比B先销毁,但全局的register_shutdown_function函数又怎么夹在它们中间被执行?!费解。

按照手册的解析,析构函数也可在调用exit时执行。

析构函数即使在使用 exit()终止脚本运行时也会被调用。在析构函数中调用 exit() 将会中止其余关闭操作的运行。

如果在函数中调用exit,它们又如何被调用的呢?

class A {

        public function __construct(){

            register_shutdown_function(array($this, 'op'));

            exit;

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

        public function op()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    class B {

        public function __construct()

        {

            register_shutdown_function(array($this, 'op'));

            $obj = new A;

        }

        public function __destruct()

        {

            echo __class__,'::',__function__,'<br/>';

        }

        public function op()

        {

            echo __class__,'::',__function__,'<br/>';

        }

    }

    register_shutdown_function(function(){echo 'global', '<br/>';});

    $b = new B;

输出:

global 

B::op 

A::op 

B::__destruct 

A::__destruct

这个顺序与上述第三个例子相似,不同的且令人不可思议的是B类的析构函数先于类A执行,难道销毁B后类A的所有引用才被全部销毁?!不得而知。

结论:
1、尽量不要在脚本中将register_shutdown_function与__destruct混搭使用,它们的行为完全不可预测。
1、因为对象在相互引用,因此我们无法测知对象几时被销毁,当需要按顺序输出内容时,不应把内容放在析构函数__destruct里;
2、尽量不要在类中注册register_shutdown_function,因为它的顺序难以预测(只有调用这个对象时才会注册函数),而且__destruct完全可以代替register_shutdown_function;
3、如果需要在脚本退出时执行相关动作,最好在脚本开始时注册register_shutdown_function,并把所有动作放在一个函数里。
敬请大家指正。

PHP 相关文章推荐
PHP执行速率优化技巧小结
Mar 15 PHP
PHP程序员编程注意事项
Apr 10 PHP
PHP extract 将数组拆分成多个变量的函数
Jun 30 PHP
php visitFile()遍历指定文件夹函数
Aug 21 PHP
PHP读取数据库并按照中文名称进行排序实现代码
Jan 29 PHP
phpnow php探针环境检测代码
Nov 04 PHP
php中rename函数用法分析
Nov 15 PHP
PHP实现的sqlite数据库连接类
Dec 12 PHP
linux中cd命令使用详解
Jan 08 PHP
PHP可变函数学习小结
Nov 29 PHP
解析PHP的Yii框架中cookie和session功能的相关操作
Mar 17 PHP
php metaphone()函数及php localeconv() 函数实例解析
May 15 PHP
PHP图片自动裁切应付不同尺寸的显示
Oct 16 #PHP
PHP 抽象方法与抽象类abstract关键字介绍及应用
Oct 16 #PHP
php开启与关闭错误提示适用于没有修改php.ini的权限
Oct 16 #PHP
php实现扫描二维码根据浏览器类型访问不同下载地址
Oct 15 #PHP
ThinkPHP基于PHPExcel导入Excel文件的方法
Oct 15 #PHP
ThinkPHP分页实例
Oct 15 #PHP
PHP中使用Session配合Javascript实现文件上传进度条功能
Oct 15 #PHP
You might like
采用PHP函数memory_get_usage获取PHP内存清耗量的方法
2011/12/06 PHP
php中get_headers函数的作用及用法的详细介绍
2013/04/27 PHP
Php中使用Select 查询语句的实例
2014/02/19 PHP
PHP 设计模式系列之 specification规格模式
2016/01/10 PHP
PHP使用PDO操作数据库的乱码问题解决方法
2016/04/08 PHP
PHP聊天室简单实现方法详解
2018/12/08 PHP
超棒的javascript页面顶部卷动广告效果
2007/12/01 Javascript
js 父窗口控制子窗口的行为-打开,关闭,重定位,回复
2010/04/20 Javascript
jQuery AJAX实现调用页面后台方法和web服务定义的方法分享
2012/03/01 Javascript
超炫的jquery仿flash导航栏特效
2014/11/11 Javascript
NodeJs中的VM模块详解
2015/05/06 NodeJs
js+html5实现可在手机上玩的拼图游戏
2015/07/17 Javascript
js编写贪吃蛇的小游戏
2020/08/24 Javascript
jquery仿QQ登录账号选择下拉框效果
2016/03/22 Javascript
在js中实现邮箱格式的验证方法(推荐)
2016/10/24 Javascript
JS+CSS实现滚动数字时钟效果
2017/12/25 Javascript
解决js相同的正则多次调用test()返回的值却不同的问题
2018/10/10 Javascript
微信小程序按钮点击动画效果的实现
2019/09/04 Javascript
原生JS实现pc端轮播图效果
2020/12/21 Javascript
[42:22]DOTA2上海特级锦标赛C组小组赛#1 OG VS Archon第一局
2016/02/27 DOTA
[43:33]EG vs Spirit Supermajor 败者组 BO3 第一场 6.4
2018/06/05 DOTA
[01:10:27]DOTA2-DPC中国联赛正赛 SAG vs XG BO3 第二场 3月5日
2021/03/11 DOTA
python实现自动登录人人网并采集信息的方法
2015/06/28 Python
通过Python实现自动填写调查问卷
2017/09/06 Python
实践Vim配置python开发环境
2018/07/02 Python
Python文件读写常见用法总结
2019/02/22 Python
Python对HTML转义字符进行反转义的实现方法
2019/04/28 Python
python取余运算符知识点详解
2019/06/27 Python
python web框架Flask实现图形验证码及验证码的动态刷新实例
2019/10/14 Python
TensorFlow基本的常量、变量和运算操作详解
2020/02/03 Python
Python如何把字典写入到CSV文件的方法示例
2020/08/23 Python
HTML5网页音乐播放器的示例代码
2017/11/09 HTML / CSS
英国奢侈品在线精品店:Hervia
2020/09/03 全球购物
企业职业病防治方案
2014/05/29 职场文书
2016重阳节红领巾广播稿
2015/12/18 职场文书
2019年大学毕业生个人自我鉴定范文大全
2019/03/21 职场文书