在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