php中__destruct与register_shutdown_function执行的先后顺序问题_php技巧
根据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__,'
';
}
}
new A;
执行结果:
A::__destruct
global
完全证实了我们的猜测,它按照对象->脚本的顺序被执行了。
但如果我们在对象中注册了register_shutdown_function呢?它还是一样的顺序吗?!
class A {
public function __construct(){
register_shutdown_function(function(){echo 'local', '
';});
}
public function __destruct()
{
echo __class__,'::',__function__,'
';
}
}
new A;
结果:
local
A::__destruct
可以看到register_shutdown_function先被调用了,最后才是执行对象的__destruct。这表明register_shutdown_function注册的函数被当作类中的一个方法?!不得而知,这可能需要查看php源代码才能解析了。
我们可以扩大范围查看情况:
register_shutdown_function(function(){echo 'global', '
';});
class A {
public function __construct(){
register_shutdown_function(array($this, 'op'));
}
public function __destruct()
{
echo __class__,'::',__function__,'
';
}
public function op()
{
echo __class__,'::',__function__,'
';
}
}
class B {
public function __construct()
{
register_shutdown_function(array($this, 'op'));
$obj = new A;
}
public function __destruct()
{
echo __class__,'::',__function__,'
';
}
public function op()
{
echo __class__,'::',__function__,'
';
}
}
$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__,'
';
}
}
class B {
public function __construct()
{
$obj = new A;
}
public function __destruct()
{
echo __class__,'::',__function__,'
';
}
}
register_shutdown_function(function(){echo 'global', '
';});
输出:
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__,'
';
}
public function op()
{
echo __class__,'::',__function__,'
';
}
}
class B {
public function __construct()
{
register_shutdown_function(array($this, 'op'));
$obj = new A;
}
public function __destruct()
{
echo __class__,'::',__function__,'
';
}
public function op()
{
echo __class__,'::',__function__,'
';
}
}
register_shutdown_function(function(){echo 'global', '
';});
$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,并把所有动作放在一个函数里。
敬请大家指正。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











Laravelは、直感的なフラッシュメソッドを使用して、一時的なセッションデータの処理を簡素化します。これは、アプリケーション内に簡単なメッセージ、アラート、または通知を表示するのに最適です。 データは、デフォルトで次の要求のためにのみ持続します。 $リクエスト -

PHPクライアントURL(CURL)拡張機能は、開発者にとって強力なツールであり、リモートサーバーやREST APIとのシームレスな対話を可能にします。尊敬されるマルチプロトコルファイル転送ライブラリであるLibcurlを活用することにより、PHP Curlは効率的なexecuを促進します

Laravelは簡潔なHTTP応答シミュレーション構文を提供し、HTTP相互作用テストを簡素化します。このアプローチは、テストシミュレーションをより直感的にしながら、コード冗長性を大幅に削減します。 基本的な実装は、さまざまな応答タイプのショートカットを提供します。 Illuminate \ support \ facades \ httpを使用します。 http :: fake([[ 'google.com' => 'hello world'、 'github.com' => ['foo' => 'bar']、 'forge.laravel.com' =>

顧客の最も差し迫った問題にリアルタイムでインスタントソリューションを提供したいですか? ライブチャットを使用すると、顧客とのリアルタイムな会話を行い、すぐに問題を解決できます。それはあなたがあなたのカスタムにより速いサービスを提供することを可能にします

記事では、PHP 5.3で導入されたPHPの後期静的結合(LSB)について説明し、より柔軟な継承を求める静的メソッドコールのランタイム解像度を可能にします。 LSBの実用的なアプリケーションと潜在的なパフォーマ

PHPロギングは、Webアプリケーションの監視とデバッグ、および重要なイベント、エラー、ランタイムの動作をキャプチャするために不可欠です。システムのパフォーマンスに関する貴重な洞察を提供し、問題の特定に役立ち、より速いトラブルシューティングをサポートします

ストレージ:: Laravelフレームワークのダウンロード方法は、ファイルストレージの抽象化を管理しながら、ファイルのダウンロードを安全に処理するための簡潔なAPIを提供します。 サンプルコントローラーでストレージ::ダウンロード()を使用する例は次のとおりです。

Laravelは、着信リクエストでHTTP動詞処理を簡素化し、アプリケーション内の多様な運用管理を合理化します。 Method()およびisMethod()メソッドは、リクエストタイプを効率的に識別および検証します。 この機能は、建物に不可欠です
