php中__destruct與register_shutdown_function執(zhí)行的先后順序問(wèn)題
根據(jù)php手冊(cè)的解析。
__destruct是
析構(gòu)函數(shù)會(huì)在到某個(gè)對(duì)象的所有引用都被刪除或者當(dāng)對(duì)象被顯式銷(xiāo)毀時(shí)執(zhí)行。
而register_shutdown_function是
Registers a callback to be executed after script execution finishes or exit() is called. 注冊(cè)一個(gè)回調(diào)函數(shù),此函數(shù)在腳本運(yùn)行完畢或調(diào)用exit()時(shí)執(zhí)行。
從字面上理解,__destruct是對(duì)象層面的,而register_shutdown_function是整個(gè)腳本層面的,理應(yīng)register_shutdown_function的級(jí)別更高,其所注冊(cè)的函數(shù)也應(yīng)最后執(zhí)行。為證實(shí)我們的猜測(cè),我們寫(xiě)一段腳本:
register_shutdown_function(function(){echo 'global';});
class A {
public function __construct(){
}
public function __destruct()
{
echo __class__,'::',__function__,'<br/>';
}
}
new A;
執(zhí)行結(jié)果:
A::__destruct
global
完全證實(shí)了我們的猜測(cè),它按照對(duì)象->腳本的順序被執(zhí)行了。
但如果我們?cè)趯?duì)象中注冊(cè)了register_shutdown_function呢?它還是一樣的順序嗎?!
class A {
public function __construct(){
register_shutdown_function(function(){echo 'local', '<br/>';});
}
public function __destruct()
{
echo __class__,'::',__function__,'<br/>';
}
}
new A;
結(jié)果:
local
A::__destruct
可以看到register_shutdown_function先被調(diào)用了,最后才是執(zhí)行對(duì)象的__destruct。這表明register_shutdown_function注冊(cè)的函數(shù)被當(dāng)作類(lèi)中的一個(gè)方法?!不得而知,這可能需要查看php源代碼才能解析了。
我們可以擴(kuò)大范圍查看情況:
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;
我們?cè)谌肿?cè)一個(gè)register_shutdown_function函數(shù),在類(lèi)AB中又各注冊(cè)了一個(gè),而且類(lèi)中分別還有析構(gòu)方法。最后運(yùn)行結(jié)果會(huì)怎樣呢?
global
B::op
A::op
A::__destruct
B::__destruct
結(jié)果完全顛覆了我們的想像,register_shutdown_function函數(shù)無(wú)論在類(lèi)中注冊(cè)還是在全局注冊(cè),它都是先被執(zhí)行,類(lèi)中執(zhí)行的順序就是它們被注冊(cè)的先后順序。如果我們?cè)僮屑?xì)研究,全局的register_shutdown_function函數(shù)無(wú)論放在前面還是后面都是這個(gè)結(jié)果,事情似乎有了結(jié)果,那就是register_shutdown_function比__destruct先執(zhí)行,全局的register_shutdown_function函數(shù)又先于類(lèi)中注冊(cè)的register_shutdown_function先執(zhí)行。
且慢,我無(wú)法接受這個(gè)結(jié)果,按照這樣的結(jié)論,難道說(shuō)腳本已經(jīng)結(jié)束后還可以再執(zhí)行__destruct?!因此,我還要繼續(xù)驗(yàn)證這個(gè)結(jié)論---去掉類(lèi)中注冊(cè)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
結(jié)果令人茫然,A、B兩個(gè)類(lèi)的析構(gòu)函數(shù)執(zhí)行順序無(wú)可質(zhì)疑,因?yàn)锽中調(diào)用了A,類(lèi)A肯定比B先銷(xiāo)毀,但全局的register_shutdown_function函數(shù)又怎么夾在它們中間被執(zhí)行?!費(fèi)解。
按照手冊(cè)的解析,析構(gòu)函數(shù)也可在調(diào)用exit時(shí)執(zhí)行。
析構(gòu)函數(shù)即使在使用 exit()終止腳本運(yùn)行時(shí)也會(huì)被調(diào)用。在析構(gòu)函數(shù)中調(diào)用 exit() 將會(huì)中止其余關(guān)閉操作的運(yùn)行。
如果在函數(shù)中調(diào)用exit,它們又如何被調(diào)用的呢?
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
這個(gè)順序與上述第三個(gè)例子相似,不同的且令人不可思議的是B類(lèi)的析構(gòu)函數(shù)先于類(lèi)A執(zhí)行,難道銷(xiāo)毀B后類(lèi)A的所有引用才被全部銷(xiāo)毀?!不得而知。
結(jié)論:
1、盡量不要在腳本中將register_shutdown_function與__destruct混搭使用,它們的行為完全不可預(yù)測(cè)。
1、因?yàn)閷?duì)象在相互引用,因此我們無(wú)法測(cè)知對(duì)象幾時(shí)被銷(xiāo)毀,當(dāng)需要按順序輸出內(nèi)容時(shí),不應(yīng)把內(nèi)容放在析構(gòu)函數(shù)__destruct里;
2、盡量不要在類(lèi)中注冊(cè)register_shutdown_function,因?yàn)樗捻樞螂y以預(yù)測(cè)(只有調(diào)用這個(gè)對(duì)象時(shí)才會(huì)注冊(cè)函數(shù)),而且__destruct完全可以代替register_shutdown_function;
3、如果需要在腳本退出時(shí)執(zhí)行相關(guān)動(dòng)作,最好在腳本開(kāi)始時(shí)注冊(cè)register_shutdown_function,并把所有動(dòng)作放在一個(gè)函數(shù)里。
敬請(qǐng)大家指正。
- PHP register_shutdown_function函數(shù)的深入解析
- php ignore_user_abort與register_shutdown_function 使用方法
- PHP register_shutdown_function()函數(shù)的使用示例
- PHP中使用register_shutdown_function函數(shù)截獲fatal error示例
- PHP錯(cuò)誤處理函數(shù)register_shutdown_function使用示例
- php register_shutdown_function函數(shù)詳解
- PHP中register_shutdown_function函數(shù)的基礎(chǔ)介紹與用法詳解
相關(guān)文章
PHP pthreads v3下的Volatile簡(jiǎn)介與使用方法示例
這篇文章主要介紹了PHP pthreads v3下的Volatile簡(jiǎn)介與使用方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了PHP pthreads v3下Volatile的功能、原理、使用方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-02-02thinkphp框架實(shí)現(xiàn)刪除和批量刪除
這篇文章主要為大家詳細(xì)介紹了thinkPHP框架實(shí)現(xiàn)刪除和批量刪除的相關(guān)資料,需要的朋友可以參考下2016-06-06php的list()的一步操作給一組變量進(jìn)行賦值的使用
我們?cè)谇懈钭址赡軙?huì)把切出來(lái)的數(shù)組一個(gè)一個(gè)付給每個(gè)變量,很麻煩的,我們可以用list()函數(shù)來(lái)完成2011-05-05PHP實(shí)現(xiàn)根據(jù)圖片色界在不同位置加水印的方法
這篇文章主要介紹了PHP實(shí)現(xiàn)根據(jù)圖片色界在不同位置加水印的方法,涉及php使用MagickWand模塊操作圖片添加水印的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-08-08php實(shí)現(xiàn)四舍五入的方法小結(jié)
這篇文章主要介紹了php實(shí)現(xiàn)四舍五入的方法,實(shí)例總結(jié)了php實(shí)現(xiàn)四舍五入的三種常用方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03PHP編碼轉(zhuǎn)換函數(shù) 自動(dòng)轉(zhuǎn)換字符集支持?jǐn)?shù)組轉(zhuǎn)換
當(dāng)我們?cè)诮邮芪粗蛻?hù)端提交的數(shù)據(jù),由于各客戶(hù)端的編碼不統(tǒng)一,但在我們的服務(wù)器端最終只能以一種編碼方式來(lái)處理,這種情況下就會(huì)涉及到編碼轉(zhuǎn)換問(wèn)題2012-12-12php性能優(yōu)化進(jìn)階不要在for循環(huán)中操作DB
這篇文章主要為大家介紹了php性能優(yōu)化進(jìn)階不要在for循環(huán)中操作DB,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06讓Nginx支持ThinkPHP的URL重寫(xiě)和PATHINFO的方法分享
ThinkPHP支持通過(guò)PATHINFO和URL rewrite的方式來(lái)提供友好的URL,只需要在配置文件中設(shè)置 'URL_MODEL' => 2 即可。在A(yíng)pache下只需要開(kāi)啟mod_rewrite模塊就可以正常訪(fǎng)問(wèn)了,但是Nginx中默認(rèn)是不支持PATHINFO的,所以我們需要修改nginx.conf文件。2011-08-08php計(jì)算數(shù)組不為空元素個(gè)數(shù)的方法
本文為大家介紹下php計(jì)算數(shù)組不為空元素個(gè)數(shù)的方法,需要的朋友可以參考下2014-01-01php編寫(xiě)一個(gè)簡(jiǎn)單的路由類(lèi)
php編寫(xiě)一個(gè)簡(jiǎn)單的路由類(lèi),需要的朋友可以參考下。2011-04-04