dongmeng2687 2017-04-07 22:42 采纳率: 0%
浏览 49

MySQLi在析构函数中关闭,立即触发

I've recently been redesigning an archaic PHP application, which had most of it's code in 1000+ line long PHP files for each page. I first started by refactoring much of the code into classes. I recently have been working on database connections, and began writing a class for it. I decided to throw $mysqli->close() into the destructor (I'm using the OO approach).

Unfortunately I've almost immediately ran in to problems with it. Instead of the MySQLi connection closing when the page finishes rendering (or when there are no more references to the DB object), it just immediately closes. I've tested this through writing a simple test:

$db = new DBConnect(); //My abstraction class
$db->getSQL()->query("SELECT 1"); //Query fails. Error message states connection closed.

My destructor looks like this:

public function __destruct() {
    $this->mysqli->close();
}

My constructor looks like this:

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.
     }
}

I know it's not some other bit of code closing the connection, since if I comment out the $this->mysqli->close() line, then the code works as expected. It seems to me that the destructor fires immediately which is not the desired behavior. Am I misunderstanding how they are intended to work?

  • 写回答

1条回答 默认 最新

  • duanqian1888 2017-04-07 22:55
    关注

    I was able to reproduce the behavior you described with the code:

    <?php
    
    class DestructorTest {
        public function getter() {
            print "DestructorTest::getter() called
    ";
            return new CallableTest();
        }
    
        public function __destruct() {
            print "DestructorTest destructor called
    ";
        }
    }
    
    class CallableTest {
        public function method() {
            print "CallableTest::method() called
    ";
        }
    }
    
    (new DestructorTest())->getter()->method();
    

    which prints:

    DestructorTest::getter() called
    DestructorTest destructor called
    CallableTest::method() called
    

    The long and the short of it is: A destructor may be called as soon as there are no references to an object. This can even happen in the middle of a line -- in this case, for instance, after DestructorTest:getter() is called, the DestructorTest object is no longer reachable, so it is destroyed.

    My advice:

    • You don't need to close MySQLi handles. They already have an internal destructor which will close them when they are garbage-collected.

    • In fact: avoid writing destructor methods in general. (This applies to many programming languages, not just PHP.) They are frequently not called when you expect them to be, and have a tendency to cause strange, hard-to-debug behavior.

    • If you're going to ignore this and write a destructor anyways, then you need to make sure that you don't inadvertently destroy something which you've "leaked" a reference to outside your class. In the case of your code, you've allowed code outside the class to get a reference to your MySQLi handle, but you're destroying that handle as soon as your object goes away -- even if the handle is still being used outside.

    评论

报告相同问题?

悬赏问题

  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大
  • ¥15 Oracle中如何从clob类型截取特定字符串后面的字符
  • ¥15 想通过pywinauto自动电机应用程序按钮,但是找不到应用程序按钮信息
  • ¥15 如何在炒股软件中,爬到我想看的日k线
  • ¥15 seatunnel 怎么配置Elasticsearch
  • ¥15 PSCAD安装问题 ERROR: Visual Studio 2013, 2015, 2017 or 2019 is not found in the system.
  • ¥15 (标签-MATLAB|关键词-多址)
  • ¥15 关于#MATLAB#的问题,如何解决?(相关搜索:信噪比,系统容量)
  • ¥500 52810做蓝牙接受端
  • ¥15 基于PLC的三轴机械手程序