1 <?php rcs_id('$Id: DbSession.php,v 1.10 2004-04-18 01:11:51 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;
40 //Fixme: E_USER_WARNING ignored!
41 trigger_error(sprintf(
42 _("Your WikiDB DB backend '%s' cannot be used for DB_Session. Set USE_DB_SESSION to false."),
43 $db_type), E_USER_WARNING);
46 function currentSessions() {
47 return $this->_backend->currentSessions();
49 function query($sql) {
50 return $this->_backend->query($sql);
52 function quote($string) {
53 return $this->_backend->quote($string);
61 var $_backend_type = "SQL";
63 function DB_Session_SQL ($dbh, $table) {
66 $this->_table = $table;
68 ini_set('session.save_handler','user');
69 session_module_name('user'); // new style
70 session_set_save_handler(array(&$this, 'open'),
71 array(&$this, 'close'),
72 array(&$this, 'read'),
73 array(&$this, 'write'),
74 array(&$this, 'destroy'),
81 $this->_connected = (bool)$dbh->connection;
82 if (!$this->_connected) {
83 $res = $dbh->connect($dbh->dsn);
84 if (DB::isError($res)) {
85 error_log("PhpWiki::DB_Session::_connect: " . $res->getMessage());
91 function query($sql) {
92 return $this->_dbh->query($sql);
95 function quote($string) {
96 return $this->_dbh->quote($string);
99 function _disconnect() {
100 if (0 and $this->_connected)
101 $this->_dbh->disconnect();
107 * Actually this function is a fake for session_set_save_handle.
108 * @param string $save_path a path to stored files
109 * @param string $session_name a name of the concrete file
110 * @return boolean true just a variable to notify PHP that everything
114 function open ($save_path, $session_name) {
115 //$this->log("_open($save_path, $session_name)");
122 * This function is called just after <i>write</i> call.
124 * @return boolean true just a variable to notify PHP that everything
129 //$this->log("_close()");
134 * Reads the session data from DB.
136 * @param string $id an id of current session
140 function read ($id) {
141 //$this->log("_read($id)");
142 $dbh = &$this->_connect();
143 $table = $this->_table;
144 $qid = $dbh->quote($id);
146 $res = $dbh->getOne("SELECT sess_data FROM $table WHERE sess_id=$qid");
148 $this->_disconnect();
149 if (DB::isError($res) || empty($res))
151 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
152 $res = base64_decode($res);
157 * Saves the session data into DB.
159 * Just a comment: The "write" handler is not
160 * executed until after the output stream is closed. Thus,
161 * output from debugging statements in the "write" handler
162 * will never be seen in the browser. If debugging output
163 * is necessary, it is suggested that the debug output be
164 * written to a file instead.
167 * @param string $sess_data
168 * @return boolean true if data saved successfully and false
172 function write ($id, $sess_data) {
174 $dbh = &$this->_connect();
175 $table = $this->_table;
176 $qid = $dbh->quote($id);
177 $qip = $dbh->quote($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
180 // postgres can't handle binary data in a TEXT field.
181 if (isa($dbh, 'DB_pgsql'))
182 $sess_data = base64_encode($sess_data);
183 $qdata = $dbh->quote($sess_data);
185 $res = $dbh->query("UPDATE $table"
186 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
187 . " WHERE sess_id=$qid");
189 if ($dbh->affectedRows() == 0)
190 $res = $dbh->query("INSERT INTO $table"
191 . " (sess_id, sess_data, sess_date, sess_ip)"
192 . " VALUES ($qid, $qdata, $time, $qip)");
194 $this->_disconnect();
195 return ! DB::isError($res);
199 * Destroys a session.
201 * Removes a session from the table.
204 * @return boolean true
207 function destroy ($id) {
208 $dbh = &$this->_connect();
209 $table = $this->_table;
210 $qid = $dbh->quote($id);
212 $dbh->query("DELETE FROM $table WHERE sess_id=$qid");
214 $this->_disconnect();
219 * Cleans out all expired sessions.
221 * @param int $maxlifetime session's time to live.
222 * @return boolean true
225 function gc ($maxlifetime) {
226 $dbh = &$this->_connect();
227 $table = $this->_table;
228 $threshold = time() - $maxlifetime;
230 $dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
232 $this->_disconnect();
236 // WhoIsOnline support
237 // TODO: ip-accesstime dynamic blocking API
238 function currentSessions() {
240 $dbh = &$this->_connect();
241 $table = $this->_table;
242 $res = $this->query("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
243 if (DB::isError($res) || empty($res))
245 while ($row = $res->fetchRow()) {
246 $data = $row['sess_data'];
247 $date = $row['sess_date'];
248 $ip = $row['sess_ip'];
249 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
250 $data = base64_decode($data);
251 // session_data contains the <variable name> + "|" + <packed string>
252 // we need just the wiki_user object (might be array as well)
253 $user = strstr($data,"wiki_user|");
254 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
258 $this->_disconnect();
263 // self-written adodb-sessions
264 class DB_Session_ADODB
267 var $_backend_type = "ADODB";
269 function DB_Session_ADODB ($dbh, $table) {
272 $this->_table = $table;
274 ini_set('session.save_handler','user');
275 session_module_name('user'); // new style
276 session_set_save_handler(array(&$this, 'open'),
277 array(&$this, 'close'),
278 array(&$this, 'read'),
279 array(&$this, 'write'),
280 array(&$this, 'destroy'),
281 array(&$this, 'gc'));
285 function _connect() {
287 static $parsed = false;
290 if (!$parsed) $parsed = parseDSN($DBParams['dsn']);
291 $this->_dbh = &ADONewConnection($parsed['phptype']); // Probably only MySql works just now
292 $conn = $this->_dbh->Connect($parsed['hostspec'],$parsed['username'],
293 $parsed['password'], $parsed['database']);
299 function query($sql) {
300 return $this->_dbh->Execute($sql);
303 function quote($string) {
304 return $this->_dbh->qstr($string);
307 function _disconnect() {
308 if (0 and $this->_dbh)
309 $this->_dbh->close();
315 * Actually this function is a fake for session_set_save_handle.
316 * @param string $save_path a path to stored files
317 * @param string $session_name a name of the concrete file
318 * @return boolean true just a variable to notify PHP that everything
322 function open ($save_path, $session_name) {
323 //$this->log("_open($save_path, $session_name)");
330 * This function is called just after <i>write</i> call.
332 * @return boolean true just a variable to notify PHP that everything
337 //$this->log("_close()");
342 * Reads the session data from DB.
344 * @param string $id an id of current session
348 function read ($id) {
349 //$this->log("_read($id)");
350 $dbh = &$this->_connect();
351 $table = $this->_table;
352 $qid = $dbh->qstr($id);
354 $row = $dbh->GetRow("SELECT sess_data FROM $table WHERE sess_id=$qid");
357 $this->_disconnect();
358 if (!empty($res) and preg_match('|^[a-zA-Z0-9/+=]+$|', $res))
359 $res = base64_decode($res);
364 * Saves the session data into DB.
366 * Just a comment: The "write" handler is not
367 * executed until after the output stream is closed. Thus,
368 * output from debugging statements in the "write" handler
369 * will never be seen in the browser. If debugging output
370 * is necessary, it is suggested that the debug output be
371 * written to a file instead.
374 * @param string $sess_data
375 * @return boolean true if data saved successfully and false
379 function write ($id, $sess_data) {
381 $dbh = &$this->_connect();
382 $table = $this->_table;
383 $qid = $dbh->qstr($id);
384 $qip = $dbh->qstr($GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR']);
387 // postgres can't handle binary data in a TEXT field.
388 if (isa($dbh, 'ADODB_postgres64'))
389 $sess_data = base64_encode($sess_data);
390 $qdata = $dbh->qstr($sess_data);
391 $rs = $dbh->Execute("UPDATE $table"
392 . " SET sess_data=$qdata, sess_date=$time, sess_ip=$qip"
393 . " WHERE sess_id=$qid");
394 if (! $dbh->Affected_Rows() )
395 $rs = $dbh->Execute("INSERT INTO $table"
396 . " (sess_id, sess_data, sess_date, sess_ip)"
397 . " VALUES ($qid, $qdata, $time, $qip)");
398 $result = ! $rs->EOF;
399 if ($result) $rs->free();
400 $this->_disconnect();
405 * Destroys a session.
407 * Removes a session from the table.
410 * @return boolean true
413 function destroy ($id) {
414 $dbh = &$this->_connect();
415 $table = $this->_table;
416 $qid = $dbh->qstr($id);
418 $dbh->Execute("DELETE FROM $table WHERE sess_id=$qid");
420 $this->_disconnect();
425 * Cleans out all expired sessions.
427 * @param int $maxlifetime session's time to live.
428 * @return boolean true
431 function gc ($maxlifetime) {
432 $dbh = &$this->_connect();
433 $table = $this->_table;
434 $threshold = time() - $maxlifetime;
436 $dbh->Execute("DELETE FROM $table WHERE sess_date < $threshold");
438 $this->_disconnect();
442 // WhoIsOnline support.
443 // TODO: ip-accesstime dynamic blocking API
444 function currentSessions() {
446 $dbh = &$this->_connect();
447 $table = $this->_table;
448 $rs = $this->Execute("SELECT sess_data,sess_date,sess_ip FROM $table ORDER BY sess_date DESC");
454 $row = $rs->fetchRow();
458 if (preg_match('|^[a-zA-Z0-9/+=]+$|', $data))
459 $data = base64_decode($data);
460 // session_data contains the <variable name> + "|" + <packed string>
461 // we need just the wiki_user object (might be array as well)
462 $user = strstr($data,"wiki_user|");
463 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
469 $this->_disconnect();
477 * Values: date : IP : data
482 var $_backend_type = "dba";
484 function DB_Session_dba ($dbh, $table) {
486 ini_set('session.save_handler','user');
487 session_module_name('user'); // new style
488 session_set_save_handler(array(&$this, 'open'),
489 array(&$this, 'close'),
490 array(&$this, 'read'),
491 array(&$this, 'write'),
492 array(&$this, 'destroy'),
493 array(&$this, 'gc'));
497 function quote($str) { return $str; }
498 function query($sql) { return false; }
500 function _connect() {
506 $dba_handler = 'gdbm';
509 $dbfile = "$directory/$prefix" . 'session' . '.' . $dba_handler;
510 $dbh = new DbaDatabase($dbfile, false, $dba_handler);
511 $dbh->set_timeout($timeout);
512 if (!$dbh->open('c')) {
513 trigger_error(sprintf(_("%s: Can't open dba database"), $dbfile), E_USER_ERROR);
515 $request->finish(fmt("%s: Can't open dba database", $dbfile));
522 function _disconnect() {
523 if (0 and isset($this->_dbh))
524 $this->_dbh->close();
527 function open ($save_path, $session_name) {
528 $dbh = &$this->_connect();
533 $dbh = &$this->_connect();
537 function read ($id) {
538 $dbh = &$this->_connect();
539 $result = $dbh->get($id);
543 list(,,$packed) = explode(':', $result, 3);
544 $data = unserialize($packed);
545 $this->_disconnect();
549 function write ($id, $sess_data) {
550 $dbh = &$this->_connect();
552 $ip = $GLOBALS['HTTP_SERVER_VARS']['REMOTE_ADDR'];
553 $dbh->set($id,$time.':'.$ip.':'.$sess_data);
554 $this->_disconnect();
558 function destroy ($id) {
559 $dbh = &$this->_connect();
561 $this->_disconnect();
565 function gc ($maxlifetime) {
566 $dbh = &$this->_connect();
567 $threshold = time() - $maxlifetime;
568 for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
569 $result = $dbh->get($id);
570 list($date,,) = explode(':', $result, 3);
571 //$dbh->query("DELETE FROM $table WHERE sess_date < $threshold");
572 if ($date < $threshold)
575 $this->_disconnect();
579 // WhoIsOnline support.
580 // TODO: ip-accesstime dynamic blocking API
581 function currentSessions() {
583 $dbh = &$this->_connect();
584 for ($id = $dbh->firstkey(); $id !== false; $id = $dbh->nextkey()) {
585 $result = $dbh->get($id);
586 list($date,$ip,$packed) = explode(':', $result, 3);
587 $data = unserialize($packed);
588 // session_data contains the <variable name> + "|" + <packed string>
589 // we need just the wiki_user object (might be array as well)
590 $user = strstr($data,"wiki_user|");
591 $sessions[] = array('wiki_user' => substr($user,10), // from "O:" onwards
595 $this->_disconnect();
605 // c-hanging-comment-ender-p: nil
606 // indent-tabs-mode: nil