1 <?php rcs_id('$Id: DbSession.php,v 1.11 2004-04-19 18:27:45 rurban Exp $');
4 * Store sessions data in Pear DB / ADODB ....
8 * Originally by Stanislav Shramko <stanis@movingmail.com>
9 * Minor rewrite by Reini Urban <rurban@x-ray.at> for Phpwiki.
10 * Quasi-major rewrite/decruft/fix by Jeff Dairiki <dairiki@dairiki.org>.
19 * Pear DB handle, or WikiDB object (from which the Pear DB handle will
22 * @param string $table
23 * Name of SQL table containing session data.
25 function DB_Session(&$dbh, $table = 'session') {
26 // Coerce WikiDB to PearDB or ADODB.
27 // Todo: adodb/dba handlers
28 $db_type = $GLOBALS['DBParams']['dbtype'];
29 if (isa($dbh, 'WikiDB')) {
30 $backend = &$dbh->_backend;
31 $db_type = substr(get_class($dbh),7);
32 $class = "DB_Session_".$db_type;
33 if (class_exists($class)) {
34 $this->_backend = new $class($backend->_dbh, $table);
35 return $this->_backend;
38 //Fixme: E_USER_WARNING ignored!
39 trigger_error(sprintf(
40 _("Your WikiDB DB backend '%s' cannot be used for DB_Session. Set USE_DB_SESSION to false."),
41 $db_type), E_USER_WARNING);
45 function currentSessions() {
46 return $this->_backend->currentSessions();
48 function query($sql) {
49 return $this->_backend->query($sql);
51 function quote($string) {
52 return $this->_backend->quote($string);
60 var $_backend_type = "SQL";
62 function DB_Session_SQL (&$dbh, $table) {
65 $this->_table = $table;
67 ini_set('session.save_handler','user');
68 session_module_name('user'); // new style
69 session_set_save_handler(array(&$this, 'open'),
70 array(&$this, 'close'),
71 array(&$this, 'read'),
72 array(&$this, 'write'),
73 array(&$this, 'destroy'),
80 $this->_connected = is_resource($dbh->connection);
81 if (!$this->_connected) {
82 $res = $dbh->connect($dbh->dsn);
83 if (DB::isError($res)) {
84 error_log("PhpWiki::DB_Session::_connect: " . $res->getMessage());
90 function query($sql) {
91 return $this->_dbh->query($sql);
94 function quote($string) {
95 return $this->_dbh->quote($string);
98 function _disconnect() {
99 if (0 and $this->_connected)
100 $this->_dbh->disconnect();
106 * Actually this function is a fake for session_set_save_handle.
107 * @param string $save_path a path to stored files
108 * @param string $session_name a name of the concrete file
109 * @return boolean true just a variable to notify PHP that everything
113 function open ($save_path, $session_name) {
114 //$this->log("_open($save_path, $session_name)");
121 * This function is called just after <i>write</i> call.
123 * @return boolean true just a variable to notify PHP that everything
128 //$this->log("_close()");
133 * Reads the session data from DB.
135 * @param string $id an id of current session
139 function read ($id) {
140 //$this->log("_read($id)");
141 $dbh = &$this->_connect();
142 $table = $this->_table;
143 $qid = $dbh->quote($id);
145 $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
147 $this->_disconnect();
148 if (DB::isError($res) || empty($res))
150 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
151 $res = base64_decode($res);
156 * Saves the session data into DB.
158 * Just a comment: The "write" handler is not
159 * executed until after the output stream is closed. Thus,
160 * output from debugging statements in the "write" handler
161 * will never be seen in the browser. If debugging output
162 * is necessary, it is suggested that the debug output be
163 * written to a file instead.
166 * @param string $sess_data
167 * @return boolean true if data saved successfully and false
171 function write ($id, $sess_data) {
173 $dbh = &$this->_connect();
174 $table = $this->_table;
175 $qid = $dbh->quote($id);
176 $qip = $dbh->quote($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
179 // postgres can't handle binary data in a TEXT field.
180 if (isa($dbh, 'DB_pgsql'))
181 $sess_data = base64_encode($sess_data);
182 $qdata = $dbh->quote($sess_data);
184 $res = $dbh->query("UPDATE $table"
185 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
186 . " WHERE sess_id=$qid");
188 if ($dbh->affectedRows() == 0)
189 $res = $dbh->query("INSERT INTO $table"
190 . " (sess_id, sess_data, sess_date, sess_ip)"
191 . " VALUES ($qid, $qdata, $time, $qip)");
193 $this->_disconnect();
194 return ! DB::isError($res);
198 * Destroys a session.
200 * Removes a session from the table.
203 * @return boolean true
206 function destroy ($id) {
207 $dbh = &$this->_connect();
208 $table = $this->_table;
209 $qid = $dbh->quote($id);
211 $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
213 $this->_disconnect();
218 * Cleans out all expired sessions.
220 * @param int $maxlifetime session's time to live.
221 * @return boolean true
224 function gc ($maxlifetime) {
225 $dbh = &$this->_connect();
226 $table = $this->_table;
227 $threshold = time() - $maxlifetime;
229 $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
231 $this->_disconnect();
235 // WhoIsOnline support
236 // TODO: ip-accesstime dynamic blocking API
237 function currentSessions() {
239 $dbh = &$this->_connect();
240 $table = $this->_table;
241 $res = $dbh->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
242 if (DB::isError($res) || empty($res))
244 while ($row = $res->fetchRow()) {
245 $data = $row['sess_data'];
246 $date = $row['sess_date'];
247 $ip = $row['sess_ip'];
248 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
249 $data = base64_decode($data);
250 // session_data contains the <variable name> + "|" + <packed string>
251 // we need just the wiki_user object (might be array as well)
252 $user = strstr($data,"wiki_user|");
253 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
257 $this->_disconnect();
262 // self-written adodb-sessions
263 class DB_Session_ADODB
266 var $_backend_type = "ADODB";
268 function DB_Session_ADODB ($dbh, $table) {
271 $this->_table = $table;
273 ini_set('session.save_handler','user');
274 session_module_name('user'); // new style
275 session_set_save_handler(array(&$this, 'open'),
276 array(&$this, 'close'),
277 array(&$this, 'read'),
278 array(&$this, 'write'),
279 array(&$this, 'destroy'),
280 array(&$this, 'gc'));
284 function _connect() {
286 static $parsed = false;
289 if (!$parsed) $parsed = parseDSN($DBParams['dsn']);
290 $this->_dbh = &ADONewConnection($parsed['phptype']); // Probably only MySql works just now
291 $this->_dbh->Connect($parsed['hostspec'],$parsed['username'],
292 $parsed['password'], $parsed['database']);
298 function query($sql) {
299 return $this->_dbh->Execute($sql);
302 function quote($string) {
303 return $this->_dbh->qstr($string);
306 function _disconnect() {
307 if (0 and $this->_dbh)
308 $this->_dbh->close();
314 * Actually this function is a fake for session_set_save_handle.
315 * @param string $save_path a path to stored files
316 * @param string $session_name a name of the concrete file
317 * @return boolean true just a variable to notify PHP that everything
321 function open ($save_path, $session_name) {
322 //$this->log("_open($save_path, $session_name)");
329 * This function is called just after <i>write</i> call.
331 * @return boolean true just a variable to notify PHP that everything
336 //$this->log("_close()");
341 * Reads the session data from DB.
343 * @param string $id an id of current session
347 function read ($id) {
348 //$this->log("_read($id)");
349 $dbh = &$this->_connect();
350 $table = $this->_table;
351 $qid = $dbh->qstr($id);
353 $row = $dbh->GetRow("SELECT sess_data FROM $table WHERE sess_id=$qid");
356 $this->_disconnect();
357 if (!empty($res) and preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
358 $res = base64_decode($res);
363 * Saves the session data into DB.
365 * Just a comment: The "write" handler is not
366 * executed until after the output stream is closed. Thus,
367 * output from debugging statements in the "write" handler
368 * will never be seen in the browser. If debugging output
369 * is necessary, it is suggested that the debug output be
370 * written to a file instead.
373 * @param string $sess_data
374 * @return boolean true if data saved successfully and false
378 function write ($id, $sess_data) {
380 $dbh = &$this->_connect();
381 $table = $this->_table;
382 $qid = $dbh->qstr($id);
383 $qip = $dbh->qstr($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
386 // postgres can't handle binary data in a TEXT field.
387 if (isa($dbh, 'ADODB_postgres64'))
388 $sess_data = base64_encode($sess_data);
389 $qdata = $dbh->qstr($sess_data);
390 $rs = $dbh->Execute("UPDATE $table"
391 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
392 . " WHERE sess_id=$qid");
393 if (! $dbh->Affected_Rows() )
394 $rs = $dbh->Execute("INSERT INTO $table"
395 . " (sess_id, sess_data, sess_date, sess_ip)"
396 . " VALUES ($qid, $qdata, $time, $qip)");
397 $result = ! $rs->EOF;
398 if ($result) $rs->free();
399 $this->_disconnect();
404 * Destroys a session.
406 * Removes a session from the table.
409 * @return boolean true
412 function destroy ($id) {
413 $dbh = &$this->_connect();
414 $table = $this->_table;
415 $qid = $dbh->qstr($id);
417 $dbh->Execute("DELETE FROM $table WHERE sess_id=$qid");
419 $this->_disconnect();
424 * Cleans out all expired sessions.
426 * @param int $maxlifetime session's time to live.
427 * @return boolean true
430 function gc ($maxlifetime) {
431 $dbh = &$this->_connect();
432 $table = $this->_table;
433 $threshold = time() - $maxlifetime;
435 $dbh->Execute("DELETE FROM $table WHERE sess_date < $threshold");
437 $this->_disconnect();
441 // WhoIsOnline support.
442 // TODO: ip-accesstime dynamic blocking API
443 function currentSessions() {
445 $dbh = &$this->_connect();
446 $table = $this->_table;
447 $rs = $this->Execute("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
453 $row = $rs->fetchRow();
457 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
458 $data = base64_decode($data);
459 // session_data contains the <variable name> + "|" + <packed string>
460 // we need just the wiki_user object (might be array as well)
461 $user = strstr($data,"wiki_user|");
462 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
468 $this->_disconnect();
476 * Values: date : IP : data
481 var $_backend_type = "dba";
483 function DB_Session_dba (&$dbh, $table) {
485 ini_set('session.save_handler','user');
486 session_module_name('user'); // new style
487 session_set_save_handler(array(&$this, 'open'),
488 array(&$this, 'close'),
489 array(&$this, 'read'),
490 array(&$this, 'write'),
491 array(&$this, 'destroy'),
492 array(&$this, 'gc'));
496 function quote($str) { return $str; }
497 function query($sql) { return false; }
499 function _connect() {
505 $dba_handler = 'gdbm';
508 $dbfile = "$directory/$prefix" . 'session' . '.' . $dba_handler;
509 $dbh = new DbaDatabase($dbfile, false, $dba_handler);
510 $dbh->set_timeout($timeout);
511 if (!$dbh->open('c')) {
512 trigger_error(sprintf(_("%s: Can't open dba database"), $dbfile), E_USER_ERROR);
514 $request->finish(fmt("%s: Can't open dba database", $dbfile));
521 function _disconnect() {
522 if (0 and isset($this->_dbh))
523 $this->_dbh->close();
526 function open ($save_path, $session_name) {
527 $dbh = &$this->_connect();
532 $dbh = &$this->_connect();
536 function read ($id) {
537 $dbh = &$this->_connect();
538 $result = $dbh->get($id);
542 list(,,$packed) = explode(':', $result, 3);
543 $data = unserialize($packed);
544 $this->_disconnect();
548 function write ($id, $sess_data) {
549 $dbh = &$this->_connect();
551 $ip = $GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR'];
552 $dbh->set($id,$time.':'.$ip.':'.$sess_data);
553 $this->_disconnect();
557 function destroy ($id) {
558 $dbh = &$this->_connect();
560 $this->_disconnect();
564 function gc ($maxlifetime) {
565 $dbh = &$this->_connect();
566 $threshold = time() - $maxlifetime;
567 for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
568 $result = $dbh->get($id);
569 list($date,,) = explode(':', $result, 3);
570 //$dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
571 if ($date < $threshold)
574 $this->_disconnect();
578 // WhoIsOnline support.
579 // TODO: ip-accesstime dynamic blocking API
580 function currentSessions() {
582 $dbh = &$this->_connect();
583 for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
584 $result = $dbh->get($id);
585 list($date,$ip,$packed) = explode(':', $result, 3);
586 $data = unserialize($packed);
587 // session_data contains the <variable name> + "|" + <packed string>
588 // we need just the wiki_user object (might be array as well)
589 $user = strstr($data,"wiki_user|");
590 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
594 $this->_disconnect();
604 // c-hanging-comment-ender-p: nil
605 // indent-tabs-mode: nil