使用 PDO 将会话存储在数据库中的 PHP 不会产生我预期的错误

Posted

技术标签:

【中文标题】使用 PDO 将会话存储在数据库中的 PHP 不会产生我预期的错误【英文标题】:PHP using PDO to store session in DB doesnt produce the errors i expected 【发布时间】:2013-03-01 11:59:24 【问题描述】:

已解决: 答案在第二个帖子中

我尝试使用 PDO 将会话存储在 DB 中,但它不会产生我预期的错误,请阅读我的代码。

这是我的会话处理程序类的代码:

class MySessionHandler implements SessionHandlerInterface

    protected $conn = NULL;

    public function open($savePath, $sessionName)
    
        if(is_null($this->conn))
        
            $dsn = 'mysql:host=localhost;dbname=php_advanced';
            $username = 'root';
            $password = 'password';
            try
            
                 $this->conn = new PDO($dsn, $username, $password);
                 $this->conn->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
            
            catch(PDOException $e)
            
                 $this->conn = NULL;
                 die('error in open function ' . $e->getMessage());
            
        
        return TRUE;
    

    public function close()
    

        echo '<p>close</p>';
        $this->conn = NULL;

        return TRUE;
    

    public function read($id)
    

        echo '<p>read</p>';
        $query = 'SELECT data FROM session_table WHERE session_id = :id';
        try
        
            $pdo = $this->conn->prepare($query);
            $pdo->bindValue(':id', $id);
            $pdo->execute();

            //    Kalo query berhasil nemuin id..
            if($pdo->rowCount() == 1)
            
                list($sessionData) = $pdo->fetch();

                return $sessionData;
            

            return FALSE;
        
        catch(PDOException $e)
        
            $this->conn = NULL;
            die('error in read function => ' . $e->getMessage());
        

    

    public function write($id, $data)
    

        echo '<p>write</p>';
        $query = 'REPLACE INTO session_table(session_id, data) VALUES(:id, :data)';

        try
        
            $pdo = $this->conn->prepare($query);
            $pdo->bindValue(':id', $id);
            $pdo->bindValue(':data', $data);
            $pdo->execute();

            // return the value whether its success or not
            return (bool)$pdo->rowCount();
        
        catch(PDOException $e)
        
            $this->conn = NULL;
            die('error in write function => ' . $e->getMessage());
        
    

    public function destroy($id)
    

        echo '<p>destroy</p>';
        $query = 'DELETE FROM session_table WHERE session_id = :id LIMIT 1';

        try
        
            $pdo = $this->conn->prepare($query);
            $pdo->bindValue(':id', $id);
            $pdo->execute();
            $_SESSION = array();
            return (bool)$pdo->rowCount();
        
        catch(PDOException $e)
        
            $this->conn = NULL;
            die('error in destroy function => ' . $e->getMessage());
        
    

    public function gc($maxLifeTime)
    

        echo '<p>garbage collection</p>';
        $query = 'DELETE FROM session_table WHERE DATE_ADD(last_accessed INTERVAL :time SECOND) < NOW()';

        try
        
            $pdo = $this->conn->prepare($query);
            $pdo->bindValue(':time', $maxLifeTime);
            $pdo->execute();

            return TRUE;
        
        catch(PDOException $e)
        
            $this->conn = NULL;
            die('error in gc function => ' . $e->getMessage());
        
    


$SessionHandler = new MySessionHandler();
session_set_save_handler($SessionHandler);
session_name('my_session');

session_start();

我故意删除了 session_write_close。这可能听起来很愚蠢,但我想获取会话错误以了解更多信息..

这里是会话脚本(使用本书的代码):

require('session_class.php');
?><!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>DB Session Test</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
<?php
// Store some dummy data in the session, if no data is present:
if (empty($_SESSION)) 

    $_SESSION['blah'] = 'umlaut';
    $_SESSION['this'] = 3615684.45;
    $_SESSION['that'] = 'blue';

    // Print a message indicating what's going on:
    echo '<p>Session data stored.</p>';

 else  // Print the already-stored data:
    echo '<p>Session Data Exists:<pre>' . print_r($_SESSION, 1) . '</pre></p>';


// Log the user out, if applicable:
if (isset($_GET['logout'])) 

    session_destroy();
    echo '<p>Session destroyed.</p>';
 else  // Otherwise, print the "Log Out" link:
    echo '<a href="session_link.php?logout=true">Log Out</a>';


// Reprint the session data:
echo '<p>Session Data:<pre>' . print_r($_SESSION, 1) . '</pre></p>';

// Complete the page:
echo '</body>
</html>';

// Write and close the session:
// session_write_close() <<<<<--- I REMOVE THIS ON PURPOSE TO GET ERROR
?>

但我没有收到任何错误,然后我尝试使用 book 的 mysqli 脚本连接数据库,它产生了我预期的错误,因为我删除了 session_write_close()..

谁能解释为什么我使用 PDO 不会产生错误?我什至不使用

register_shutdown_function('session_write_close');

在我的会话类析构函数中(故意)

注意:我这样做是故意的,因为我想了解更多。

我期望的错误就像我使用 mysqli 连接时一样(连接在脚本末尾由 php 关闭,然后会话尝试写入并关闭但没有可用的连接):

Warning: mysqli_real_escape_string() expects parameter 1 to be mysqli, null given in /var/www/ullman_advance/ch3/ullman_db.php on line 66



Warning: mysqli_real_escape_string() expects parameter 1 to be mysqli, null given in /var/www/ullman_advance/ch3/ullman_db.php on line 66



Warning: mysqli_query() expects parameter 1 to be mysqli, null given in /var/www/ullman_advance/ch3/ullman_db.php on line 67



Warning: mysqli_close() expects parameter 1 to be mysqli, null given in /var/www/ullman_advance/ch3/ullman_db.php on line 33

更新 1

我最近发现 mysqli 每次使用 mysqli_real_escape_string()mysqli_query 时都需要数据库连接,因为但我认为我的 pdo 在脚本结束时也需要 db 连接 -> db 连接关闭 -> @987654328 @将尝试写入并关闭,但由于pdo已被php关闭,因此没有db连接,但没有产生错误..

更新 2

我只是试图传递session_set_save_handler 函数回调,它会产生错误

<?php

$conn = NULL;

function open_session()

    echo '<p>open session</p>';
    global $conn;
    $_dsn = 'mysql:host=localhost;dbname=php_advanced';
    $_username = 'root';
    $_password = 'password';
    $conn = new PDO($_dsn, $_username, $_password);
    $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

    return TRUE;


function close_session()

    echo '<p>close session</p>';
    global $conn;

    $conn = NULL;
    return TRUE;


function read_session($sid)

    echo '<p>read session</p>';
    global $conn;

    $query = 'SELECT data FROM session_table WHERE session_id = :sid';
    $pdo = $conn->prepare($query);
    $pdo->bindValue(':sid', $sid, PDO::PARAM_STR);
    $pdo->execute();

    if($pdo->rowCount() == 1)
    
        list($session_data) = $pdo->fetch();
        echo '<pre>';
        print_r($session_data);
        echo '</pre>';

        return $session_data;
    
    else
    
        return '';
    



function write_session($sid, $data)

    echo '<p>write session</p>';
    global $conn;

    $query = 'REPLACE INTO session_table(session_id, data) VALUES(:sid, :data)';
    $pdo = $conn->prepare($query);
    $pdo->bindValue(':sid', $sid, PDO::PARAM_STR);
    $pdo->bindValue(':data', $data, PDO::PARAM_STR);
    $pdo->execute();

    return $pdo->rowCount();


function destroy_session($sid)

    echo '<p>destroy session </p>';
    global $conn;

    $query = 'DELETE FROM session_table WHERE session_id = :sid';
    $pdo = $conn->prepare($query);
    $pdo->bindValue(':sid', $sid, PDO::PARAM_STR);
    $pdo->execute();

    // clean the session array;
    $_SESSION = array();

    return (bool)$pdo->rowCount();



function clean_session($expire)

    echo '<p>clean session</p>';
    global $conn;

    $query = 'DELETE FROM session_table WHERE DATE_ADD(last_accessed, INTERVAL :expire SECOND) < NOW()';

    $pdo = $conn->prepare($query);
    $pdo->bindValue(':expire', $expire, PDO::PARAM_INT);
    $pdo->execute();

    return $pdo->rowCount();


session_set_save_handler('open_session', 'close_session', 'read_session', 'write_session', 'destroy_session', 'clean_session');
session_name('my_session');
session_start();

但是当我通过MySessionHandler类时,它不会因为没有连接而产生错误。

【问题讨论】:

不要使用 try..catch 和 die!这是无用的、错误的和不安全的 好吧,谢谢您的提示。顺便说一句,在 catch 块中做的好的/做法是什么 仅当您要以某种方式处理错误时才使用 catch 块。否则就放手吧。 PHP 将通知您并自动退出脚本 - 因此,无需编写 100 次捕获。 哦,好吧,这真的帮助我变得更好。谢谢!无论如何,你知道为什么我上面的 pdo 脚本不会产生错误吗? 有些问题一目了然无法回答,只能经过一些调试。如果您认为这种行为是错误的,为什么不想稍微调试一下您的代码? 【参考方案1】:

解决方案

对不起,伙计们,我的错误实际上是一个非常简单的答案,为什么 MySessionHandler 类不会在脚本末尾出现 session_write_close() 错误,

session_set_save_handler()默认注册session_write_close()register_shutdown_function()

因此,如果您想为会话创建自己的关闭功能,请使用: session_set_save_handler($SessionClass, FALSE),如果你这样做,那么你必须在你的类析构函数中提供session_write_close()

来源:http://php.net/manual/en/function.session-set-save-handler.php

感谢您的提示和关注

【讨论】:

接受你的回答,那么很明显它已经回答了。并不是说有人通过文字墙工作然后最终意识到它是徒劳的。

以上是关于使用 PDO 将会话存储在数据库中的 PHP 不会产生我预期的错误的主要内容,如果未能解决你的问题,请参考以下文章

将php变量存储在PDO中,以便在for循环中获取内容

PHP:会话中的会话?

PHP - 啥是会话变量的替代品

使用 PDO 和 PHP 将表单中的数据插入 MySQL 数据库

使用 PHP PDO 将参数传递给 SQL Server 存储过程

PHP,PDO 存储过程不返回任何值或未更改值