由会话重定向看到的对象销毁问题

探讨PHP中析构函数关闭数据库连接的问题,特别是在会话重定向时的执行顺序及解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天和同事又讨论到DB类中析构函数是否要关闭数据库连接的问题,粗略看起来貌似在DB对象销毁之前关闭数据库连接再正常不过:
[php]class DB {
    public $conn;
    public function __construct() {
        $this->conn = mysql_connect('localhost', 'root', '');
        mysql_select_db('test', $this->conn);
    }
    public function query($sql) {
        return mysql_query($sql, $this->conn);
    }
    public function __destruct() {
        echo "destruct:close<br />";
        mysql_close($this->conn);
    }
}[/php]

  执行:

[php]<?php
//class DB ..
$db = new DB;
?>[/php]

  正常打印 destruct:close 。
  再来看看下面的会话重定向到DB:

[php]<?php
class DB {
    public $conn;
    public function __construct() {
        $this->conn = mysql_connect('localhost', 'root', '');
        mysql_select_db('test', $this->conn);
    }
    public function query($sql) {
        return mysql_query($sql, $this->conn);
    }
    public function __destruct() {
        echo "destruct:close<br />";
        mysql_close($this->conn);
    }
}
class SessionHandler {
    public static $db;
    function open() {
    }
    function close() {
    }
    function read() {
    }
    function write($id, $data) {
        echo "session:write<br />";
        $sql = "REPLACE INTO session VALUES('$id', '$data')";
        self::$db->query($sql);
    }
    function destroy() {
    }
    function gc() {
    }
}
SessionHandler::$db = new DB;
session_set_save_handler(array('SessionHandler', 'open'), array('SessionHandler', 'close'), array('SessionHandler', 'read'), array('SessionHandler', 'write'), array('SessionHandler', 'destroy'), array('SessionHandler', 'gc'));
session_start();
$_SESSION['user'] = '深空';
?>[/php]

  这次析构正常,只不过会话写入出错了,析构在会话写入之前被执行了:

destruct:close
session:write
Warning: mysql_query(): 2 is not a valid MySQL-Link resource on line 12

  厄,见鬼了吧,PHP竟然在脚本执行结束后先执行析构函数,然后才处理会话。

  我再稍微修改一下 SessionHandler:

[php]<?php
class DB {
    public $conn;
    public function __construct() {
        $this->conn = mysql_connect('localhost', 'root', '');
        mysql_select_db('test', $this->conn);
    }
    public function query($sql) {
        return mysql_query($sql, $this->conn);
    }
    public function __destruct() {
        echo "destruct:close<br />";
        mysql_close($this->conn);
    }
}
class SessionHandler {
    function open() {
    }
    function close() {
    }
    function read() {
    }
    function write($id, $data) {
        global $db;
        echo "session:write<br />";
        $sql = "REPLACE INTO session VALUES('$id', '$data')";
        $db->query($sql);
    }
    function destroy() {
    }
    function gc() {
    }
}
$db = new DB();
session_set_save_handler(array('SessionHandler', 'open'), array('SessionHandler', 'close'), array('SessionHandler', 'read'), array('SessionHandler', 'write'), array('SessionHandler', 'destroy'), array('SessionHandler', 'gc'));
session_start();
$_SESSION['user'] = '深空';
?>[/php]

  输出如下:

destruct:close
session:write
Fatal error: Call to a member function query() on a non-object on line 35

  这次报错让我感到意外,根据提示可以认定35行的$db变量不是个对象,后来想想也确实应该是这样,因为__destruct方法已经执行了,那么其 实对象在write之前已经被销毁了,所以找不到$db变量已经是空的,不足为奇。我又测试了一些字符串和数组变量,发现write执行的时候,这些变量 还没有被销毁,我又做了如下修改:
[php]<?php
class DB {
    public $conn;
    public function __construct() {
        $this->conn = mysql_connect('localhost', 'root', '');
        mysql_select_db('test', $this->conn);
    }
    public function query($sql) {
        return mysql_query($sql, $this->conn);
    }
    public function __destruct() {
        echo "destruct:close<br />";
        mysql_close($this->conn);
    }
}
class SessionHandler {
    function open() {
    }
    function close() {
    }
    function read() {
    }
    function write($id, $data) {
        global $db;
        echo "session:write<br />";
        $sql = "REPLACE INTO session VALUES('$id', '$data')";
        $db['session']->query($sql);
    }
    function destroy() {
    }
    function gc() {
    }
}
$db['session'] = new DB();
session_set_save_handler(array('SessionHandler', 'open'), array('SessionHandler', 'close'), array('SessionHandler', 'read'), array('SessionHandler', 'write'), array('SessionHandler', 'destroy'), array('SessionHandler', 'gc'));
session_start();
$_SESSION['user'] = '深空';
?>[/php]

  输出:

destruct:close
session:write
Warning: mysql_query(): 2 is not a valid MySQL-Link resource on line 12

  呵呵,这次$db没有被销毁,因为它是个数组变量,后来和同事分析觉得可能是这些变量和对象的存储方式不一样,销毁的顺序也不一样导致的。不过 __destruct仍然是在write前执行,基本上可以判断会话是在很后面才执行的。要解决这个问题,只能去掉DB的__destruct方法。

  我没有去深究PHP内部对变量的处理和回收方式,只是在重定向会话处理方法的时候这个问题才显露出来,是个比较有趣的问题,发出来和大家分享我的发现主要是想让大家在碰到这个问题的时候不会太浪费时间在调试上。www.zhenaish.com

  希望明真相的高手不吝指点。

转载于:https://www.cnblogs.com/zayy/p/3454153.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值