我最近一直在重新设计一个古老的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()
行,那么代码按预期工作。在我看来,析构函数立即激发 ,这不是理想的行为。我误解了他们打算如何工作吗?
答案 0 :(得分:2)
我能够使用代码重现您描述的行为:
<?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。)当您期望它们时,它们通常不会被调用,并且倾向于导致奇怪的,难以调试的行为。
如果您无视这一点并写了一个析构函数,那么您需要确保不会无意中破坏您已泄露的内容&# 34;提到你班级以外的人。对于您的代码,您已经允许类外部的代码获取对MySQLi句柄的引用,但是一旦您的对象消失,您就会破坏该句柄 - 即使句柄仍然存在在外面使用。