当使用自定义会话处理程序并建立第二个postgres连接时,PHP7正在中断我的会话


PHP7 is breaking my sessions when custom session handler is used and a seccond postgres connection is made

在postgreSQL服务器上使用自定义会话处理程序保存会话时,打开seccond 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));
    }
}

初始化我们的处理程序和我们的会话:

$handler = new custom_session_handler();
session_set_save_handler($handler, true);
session_name('my_session');
session_start();

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

    $_SESSION['test'] = 'áéíóúñ';

在这一点上,你可以随心所欲地var_dump()你的会话,它会起作用,你可以刷新页面,它会维护会话信息,它会发挥作用,但请与我保持联系。。。

嗯,我需要检查一下冰箱:

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

哦,会话数据似乎还可以,但是。。。等等现在你的会话中断了

重新加载页面,您将获得: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(会话存储):PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu,由gcc(gcc)4.4.7 20120313(Red Hat 4.4.7-11)编译,64位

    SECCOND SERVER:PostgreSQL 8.4.20 on x86_64-redhat-linux-gnu,由GCC GCC(GCC)4.4.7 20120313(Red Hat 4.4.7-11)编译,64位

    CLIENT(php):PostgreSQL(libpq)9.4.4版

真正的问题是:

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

真正的答案是:

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

pg_escape_bytea($this->db, $data);

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

有效的变通办法(你不应该这样做):

当然,您可以将会话信息封装在base64()中,然后将其转义为字节。稍后,您将需要取消捕获并打开它。

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

额外信息:

yohgaki谁被分配到php错误票据指出了我的会话处理程序上的不同错误,他还发布了一个使用外部数据库实现良好会话处理程序的例子。如果您感兴趣,详情如下:https://bugs.php.net/bug.php?id=71088