While using a custom session handler to save sessions on a postgreSQL server open a seccond postgres connection and the session will break if any unicode chars are present!
This is my custom session handler:
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));
}
}
Init our handler along with our session:
$handler = new custom_session_handler();
session_set_save_handler($handler, true);
session_name('my_session');
session_start();
And save some data into our session:
$_SESSION['test'] = 'áéíóúñ';
At this point you can var_dump()
your session all that you want and it will work, you can refresh the page and it will maintain session info and it will work but stay with me on this...
well, I need to check the fridge:
$seccond_server = pg_connect("host=#### port=#### dbname=#### user=#### password=######### or die("No bueno on db #2");
print_r($_SESSION);
Oooh, session data seems ok but... wait... Now your session is broken!
Reload the page and you will get: PHP Warning: session_start(): Failed to decode session object. Session has been destroyed in some location at some line
Reloading the page once more will give you: PHP Warning: pg_fetch_result(): Unable to jump to row 0 on PostgreSQL result index something in some path:
You can play commenting and uncommenting that seccond pg_connect
and the session will work as long as you dont do that seccond pg_connect
Updates:
I forgot to mention that the same code works flawlessly on PHP 5.6.2
I added this problem as a comment to the PHP bug tracking system as I found something similar there: https://bugs.php.net/bug.php?id=70584
I just filled in a PHP bug report if anyone wants to follow and/or update it: https://bugs.php.net/bug.php?id=71088
Client and server info:
FIRST SERVER (session storage): PostgreSQL 9.4.4 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit
SECCOND SERVER: PostgreSQL 8.4.20 on x86_64-redhat-linux-gnu, compiled by GCC gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11), 64-bit
CLIENT(php): PostgreSQL(libpq) Version 9.4.4
BYTEA escaping use diffrent escaping methods when it comes about postgres 9 and postgres 8 and PHP does not isolate pg_ behavior @ class level.
You need to pass the right database link as the first argument of pg_escape_bytea()
so it can use the right escape method:
pg_escape_bytea($this->db, $data);
On the other hand pg_unescape()
wont take your db link as an argument.
You can of course wrap up the session info in base64()
and then escape it as bytea. Later on you will need to unescape and unwrap it.
Seems to be that the escaping methods are diffrent in how they handle multibyte strings and therefore, a base64 string wont produce any error.
Extra info:
yohgaki who is assigned to the php bug ticket pointed out diffrent errors on my session handler, he also posted and example of a well implemented session handler using external databases. If you are intrested the full details are here: https://bugs.php.net/bug.php?id=71088
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With