MySQLi 在析构函数中立即关闭
Posted
技术标签:
【中文标题】MySQLi 在析构函数中立即关闭【英文标题】:MySQLi close in destructor fires immediately 【发布时间】:2017-09-03 10:15:13 【问题描述】:我最近一直在重新设计一个古老的 php 应用程序,它的大部分代码都包含在每个页面的 1000 多行 PHP 文件中。我首先将大部分代码重构为类。我最近一直在研究数据库连接,并开始为它编写一个类。我决定将$mysqli->close()
扔到析构函数中(我正在使用OO 方法)。
不幸的是,我几乎立即就遇到了问题。当页面完成渲染时(或者当没有更多对 DB 对象的引用时)而不是关闭 MySQLi 连接,它只是立即关闭。我通过编写一个简单的测试对此进行了测试:
$db = new DBConnect(); //My abstraction class
$db->getSQL()->query("SELECT 1"); //Query fails. Error message states connection closed.
我的析构函数如下所示:
public function __destruct()
$this->mysqli->close();
我的构造函数如下所示:
public function __construct()
$this->mysqli=new mysqli(\MainConfig::$database['host'], \MainConfig::$database['user'], \MainConfig::$database['pass'], \MainConfig::$database['db']);
if($this->mysqli->connect_error)
die('Connect error (' . $this->mysqli->connect_errno . ') ' . $this->mysqli->connect_error); //This is not ever fired.
我知道这不是关闭连接的其他代码,因为如果我注释掉 $this->mysqli->close()
行,那么代码会按预期工作。在我看来,析构函数 立即 触发,这不是所需的行为。我是否误解了它们的工作原理?
【问题讨论】:
看起来有一些代码丢失导致了这种情况。 PHP 垃圾收集器不会破坏对象,除非不再引用它。 每个DBConnect
实例是否有自己的mysqli 连接对象,或者您是否缓存它们并在不同对象之间共享它们?
@b.enoit.be 项目中的所有源文件都复制了这种行为,包括那里的测试代码。我无法发布 1000 行长的源文件!
也发布构造函数。
恕我直言,通常没有必要明确关闭 mysqli 连接。如果 $db 是一个全局变量,它直到脚本结束才会消失,并且连接会自动关闭。
【参考方案1】:
我能够重现您使用代码描述的行为:
<?php
class DestructorTest
public function getter()
print "DestructorTest::getter() called\n";
return new CallableTest();
public function __destruct()
print "DestructorTest destructor called\n";
class CallableTest
public function method()
print "CallableTest::method() called\n";
(new DestructorTest())->getter()->method();
哪个打印:
DestructorTest::getter() called
DestructorTest destructor called
CallableTest::method() called
它的长短是:只要没有对对象的引用,就可以调用析构函数。这甚至可能发生在一行的中间——例如,在这种情况下,在调用 DestructorTest:getter()
之后,DestructorTest 对象不再可访问,因此它被销毁了。
我的建议:
您不需要关闭 MySQLi 句柄。它们已经有一个内部析构函数,当它们被垃圾回收时会关闭它们。
事实上:避免编写一般的析构方法。 (这适用于许多编程语言,而不仅仅是 PHP。)它们通常不会在您期望的时候被调用,并且往往会导致奇怪的、难以调试的行为。
如果你要忽略这一点并编写析构函数,那么你需要确保你不会无意中破坏你在课堂之外“泄露”了引用的东西。就您的代码而言,您已经允许类外部的代码获取对 MySQLi 句柄的引用,但是一旦您的对象消失,您就会销毁该句柄——即使句柄仍在外面使用。
【讨论】:
但是他将对象分配给变量,而不是在临时结果上调用方法。这应该保留指针并防止调用析构函数。 我同意,尽管我开始意识到在这种情况下几乎不需要析构函数。我目前完全禁用了代码工作,我可能就这样离开它。为什么如果我不关闭 MySQLi 连接,代码仍然有效,这对我来说没有意义。这不是说调用了析构函数,但对象仍然存在吗? @Barmar 这是真的。我无法根据问题中包含的代码来解释行为;我提供的代码是最接近重现问题的代码。 使用连接池来回收现有连接也是值得的。以上是关于MySQLi 在析构函数中立即关闭的主要内容,如果未能解决你的问题,请参考以下文章
为啥我必须在析构函数中调用 MPI.Finalize() ?