当使用自定义会话处理程序并建立了第二个postgres连接时,PHP7会破坏我的会话

Sol*_*rac 5 php postgresql session

使用自定义会话处理程序在postgreSQL服务器上保存会话时,打开一个第二个postgres连接,如果存在任何unicode字符,会话将中断!

这是我的自定义会话处理程序:

class custom_session_handler implements SessionHandlerInterface {

    protected $nombre_de_sesion, $tiempo_de_vida, $db;

    public function open($savePath, $sessionName) {
        $this->db = pg_connect("host=**** port=5432 dbname=**** user=**** password=***") or die('Imposible conectar con la base de datos de sesiones');
        $this->gc(time());
        $this->nombre_de_sesion = trim($sessionName);
        $this->tiempo_de_vida = pg_escape_literal(time() + 2500);
        return (isset($this->nombre_de_sesion) && strlen($this->nombre_de_sesion) > 2) ? true : false;
    }

    public function close() {
        return pg_close($this->db);
    }

    public function destroy($session_id) {
        return pg_affected_rows(pg_query($this->db, 'DELETE FROM "sesiones_soporte" WHERE session_id = ' . pg_escape_literal($session_id) . '')) ? true : false;
    }

    public function write($session_id, $session_data) {
        $escaped_id = pg_escape_literal($session_id);
        $escaped_session = pg_escape_literal(pg_escape_bytea($session_data));
        if (pg_affected_rows(pg_query($this->db, 'UPDATE "sesiones_soporte" SET "session_expira" = ' . $this->tiempo_de_vida . ', "session_byte"=' . $escaped_session . ' WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time())) == 1) {
            return true;
        } else {
            pg_query($this->db, 'INSERT INTO "sesiones_soporte" ("session_id", "session_expira", "session_byte") VALUES (' . $escaped_id . ', ' . $this->tiempo_de_vida . ', ' . $escaped_session . ' )');
            return true;
        }
        return false;
    }

    public function read($session_id) {
        $escaped_id = pg_escape_literal($session_id);
        $sesion = pg_unescape_bytea(pg_fetch_result(pg_query($this->db, 'SELECT session_byte FROM "sesiones_soporte" WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time() . ' LIMIT 1'), 0, 'session_byte'));
        !isset($sesion) ? : pg_query($this->db, 'UPDATE "sesiones_soporte" SET  "session_expira" = ' . $this->tiempo_de_vida . ' WHERE session_id = ' . $escaped_id . ' AND session_expira > ' . (int) time());
        return (isset($sesion) ? $sesion : false);
    }

    public function gc($maxlifetime) {
        return pg_affected_rows(pg_query($this->db, 'DELETE FROM "sesiones_soporte" WHERE session_expira < ' . (int) $maxlifetime));
    }

}
Run Code Online (Sandbox Code Playgroud)

在我们的会话中启动我们的处理程序

$handler = new custom_session_handler();
session_set_save_handler($handler, true);
session_name('my_session');
session_start();
Run Code Online (Sandbox Code Playgroud)

并将一些数据保存到我们的会话中:

    $_SESSION['test'] = 'áéíóúñ';
Run Code Online (Sandbox Code Playgroud)

此时你可以var_dump()在你的会话中找到你想要的所有内容,它可以正常工作,你可以刷新页面,它会保持会话信息,它会起作用但是留在我这里...

好吧,我需要检查冰箱:

$seccond_server = pg_connect("host=#### port=#### dbname=#### user=#### password=######### or die("No bueno on db #2");
    print_r($_SESSION);
Run Code Online (Sandbox Code Playgroud)

哦,会话数据似乎没问题但是......等等...... 现在你的会话已经破了!

重新加载页面,您将获得: PHP Warning: session_start(): Failed to decode session object. Session has been destroyed in some location at some line

再次重新加载页面将为您提供: PHP Warning: pg_fetch_result(): Unable to jump to row 0 on PostgreSQL result index something in some path:

您可以播放评论并取消注释该秒数pg_connect,只要您不这样做,会话就会起作用pg_connect

更新:

  • 我忘了提到相同的代码在PHP 5.6.2上完美运行

  • 我将此问题添加为对PHP错误跟踪系统的评论,因为我发现了类似的内容:https: //bugs.php.net/bug.php?id = 70584

  • 如果有人想关注和/或更新它,我只填写了一个PHP错误报告:https://bugs.php.net/bug.php?id = 71088

  • 客户端和服务器信息:

    FIRST SERVER(会话存储):x86_64-unknown-linux-gnu上的PostgreSQL 9.4.4,由gcc(GCC)4.4.7 20120313(Red Hat 4.4.7-11)编译,64位

    SECCOND服务器:x86_64-redhat-linux-gnu上的PostgreSQL 8.4.20,由GCC gcc(GCC)4.4.7 20120313(Red Hat 4.4.7-11)编译,64位

    CLIENT(php):PostgreSQL(libpq)版本9.4.4

Sol*_*rac 3

真正的问题:

当涉及到 postgres 9 和 postgres 8 时, BYTEA 转义使用不同的转义方法,并且 PHP 不会在类级别隔离 pg_ 行为。

真正的答案:

您需要传递正确的数据库链接作为第一个参数,pg_escape_bytea()以便它可以使用正确的转义方法:

pg_escape_bytea($this->db, $data);
Run Code Online (Sandbox Code Playgroud)

另一方面pg_unescape(),不会将您的数据库链接作为参数。

有效的解决方法(您不应该这样做):

当然,您可以将会话信息包装起来base64(),然后将其转义为 bytea。稍后您将需要取消转义并打开它。

转义方法似乎在处理多字节字符串的方式上有所不同,因此,base64 字符串不会产生任何错误。

额外信息:

负责 php bug 票证的yohgaki指出了我的会话处理程序上的不同错误,他还发布了使用外部数据库实现良好的会话处理程序的示例。如果您有兴趣,完整的详细信息在这里:https ://bugs.php.net/bug.php?id=71088