【问题标题】:PDO prepare silently failsPDO 准备静默失败
【发布时间】:2010-04-07 20:56:36
【问题描述】:

我正在试验 PHP 的 session_set_save_handler,我想使用 PDO 连接来存储会话数据。

我有这个函数作为写操作的回调:

function _write($id, $data) {
    logger('_WRITE ' . $id . ' ' . $data);
    try {
        $access = time();
        $sql = 'REPLACE INTO sessions SET id=:id, access=:access, data=:data';
        logger('This is the last line in this function that appears in the log.');
        $stmt = $GLOBALS['db']->prepare($sql);
        logger('This never gets logged! :(');
        $stmt->bindParam(':id', $id, PDO::PARAM_STR);
        $stmt->bindParam(':access', $access, PDO::PARAM_INT);
        $stmt->bindParam(':data', $data, PDO::PARAM_STR);
        $stmt->execute();
        $stmt->closeCursor();
        return true;
    } catch (PDOException $e) {
        logger('This is never executed.');
        logger($e->getTraceAsString());
    }
}

前两条日志消息始终显示,但紧随$stmt = $GLOBALS['db']->prepare($sql) 之后的第三条消息从未出现在日志文件中,也没有异常的痕迹。

sessions db 表保持为空。

来自_close 回调的日志消息始终存在。

这是我连接到数据库的方式:

$db = new PDO('mysql:host=' . DBHOST . ';dbname=' . DBNAME, DBUSER, DBPASS);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

我有 PHP 5.2.10。

我尝试使用“手动准备的”$sql 内容简单地运行$GLOBALS['db']->exec($sql),但它仍然默默地失败了。查询本身没问题,我可以通过 db 控制台执行它。


编辑:

在 VolkerK 发现问题后,我发现了this article,这解释了这种奇怪现象背后的原因。也许它也可以为其他人提供信息。


第二次编辑:

最不痛苦、最神奇的解决方案是我必须将以下函数调用添加到我的前端控制器(主 index.php)文件的最后:

session_write_close();

【问题讨论】:

  • 确保您已启用error_reporting(-1)
  • @Gordon:什么都没有改变。我已经有了这个设置:error_reporting(E_ALL|E_STRICT)
  • 好的。顺便说一句 -1 和你的设置一样,只是更方便记住;)

标签: php session pdo session-set-save-handler


【解决方案1】:

我的赌注是:$GLOBALS['db'] 未设置或不是 pdo 的实例(不再是?),因此出现 PHP Fatal error: Call to a member function prepare() on a non-object 并且 php 退出。

$sql = 'REPLACE INTO sessions SET id=:id, access=:access, data=:data';
logger('This is the last line in this function that appears in the log.');
if ( !isset($GLOBALS['db']) ) {
  logger('there is no globals[db]');
  return;
}
else if ( !is_object($GLOBALS['db']) ) {
  logger('globals[db] is not an object');
  return;
}
else if ( !($GLOBALS['db'] instanceof PDO) ) {
  logger('globals[db] is not a PDO object');
  return;
}
else {
  logger('globals[db] seems ok');
}

$stmt = $GLOBALS['db']->prepare($sql);
logger('This never gets logged! :(');

【讨论】:

  • 我们有一个赢家! :) 的确,我收到了“没有全局变量 [db]”的记录。我不确定为什么会这样,但这是一个非常有用的线索。非常感谢您的帮助,非常感谢。
【解决方案2】:

也许 PDO 无法识别 REPLACE INTO 语法。如果底层 DB 访问库不直接支持预准备语句,PDO 会模拟它们,并且可能的语句类型列表中可能没有 REPLACE INTO。

尝试在准备调用后立即检查$stmt->errorCode()

如果是mysql,可以尝试重写prepared statement如下:

INSERT INTO sessions (id, access, data)
VALUES(:id, :access, :data)
ON DUPLICATE KEY UDPATE
    access=VALUES(access), data=VALUES(data);

看看这是否能让你走得更远。

【讨论】:

  • 确实我使用的是 MySQL 5.1,但重写查询并没有帮助。我什至尝试将其重写为完全无害且无用的SELECT,并且在prepare 之后仍然停止,没有任何通知。因此,我无法检查errorCode。这真的很奇怪。
【解决方案3】:

我觉得无法通过$GLOBALS[] 访问资源数据类型。关于引用的处理方式或其他东西。如果你有心情迎合我的直觉,试试这个作为你的函数声明:

function _write($id, $data) {
    global $db;
    logger('_WRITE ' . $id . ' ' . $data);
    try {

而不是这个:

$stmt = $GLOBALS['db']->prepare($sql);

试试

$stmt = $db->prepare($sql);

您也可以尝试捕获普通的旧 Exceptions 而不是 PDOExceptions;它可能会被赶上。

希望这会有所帮助!

【讨论】:

  • 使用global 和使用$GLOBAL 引用全局变量绝对没有区别。普通香草Exception 也没有任何区别。感谢您尝试提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多